Download Zip file in Xamarin iOS Native - ios

I have to download a Zip File in Xamarin iOS.
URL - https://osdn.net/projects/sfnet_fotohound/downloads/sample-pictures/Sample/Sample-Pictures.zip/
As soon as i hit this URL, the download should start and the same should get saved in a particular folder in the documents directory.
How should I implement the same in Xamarin Native iOS.

You can use NSURLSession to download the zip file.
Firstly, you should find the real download link of this download site.
You can refer to this.
In Chrome - run download as normal - then go to Menu - Downloads - and you should see the direct link which was used.
Actually, your file link is https://mirrors.netix.net/sourceforge/f/fo/fotohound/sample-pictures/Sample/Sample-Pictures.zip.
Now start to code. Create download task via NSURLSession:
public void downloadTask()
{
// Your file link.
NSUrl url = NSUrl.FromString("https://mirrors.netix.net/sourceforge/f/fo/fotohound/sample-pictures/Sample/Sample-Pictures.zip");
// Configure your download session.
var config = NSUrlSessionConfiguration.DefaultSessionConfiguration;
NSUrlSession session = NSUrlSession.FromConfiguration(config, new SimpleSessionDelegate(), new NSOperationQueue());
var downloadTask = session.CreateDownloadTask(NSUrlRequest.FromUrl(url));
// Start the session.
downloadTask.Resume();
Console.WriteLine("Start DownloadTask!!!");
}
Configure the callback DidFinishDownloading:
class SimpleSessionDelegate : NSUrlSessionDownloadDelegate
{
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
// Configure your destination path. Here's saved to /Documents/ folder.
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var destinationPath = Path.Combine(documents, "Sample.zip");
if (File.Exists(location.Path))
{
NSFileManager fileManager = NSFileManager.DefaultManager;
NSError error;
// Remove the same name file in destination path.
fileManager.Remove(destinationPath, out error);
// Copy the file from the tmp directory to your destination path. The tmp file will be removed when this delegate finishes.
bool success = fileManager.Copy(location.Path, destinationPath, out error);
if (!success)
{
Console.WriteLine("Error during the copy: {0}", error.LocalizedDescription);
}
}
}
}
Now the file has been saved at the documents directory and named Sample.zip.

Related

Xamarin iOS DocumentPicker: How to import large files?

In the DidPickDocument event of UIDocumentPickerViewController I try to import/write the selected file into the app's local Documents directory.
This works fine with "small" files (e.g < 100MB) using a subclassed UIDocument class with overriden
public override bool LoadFromContents(NSObject contents, string typeName, out NSError outError)
{
outError = null;
if (contents != null)
{
Content = ((NSData)contents).ToArray();
}
...
...and by calling
MySubclassedDoc mySubclassedDoc = new MySubclassedDoc (nsurl);
bool success = await mySubclassedDoc.OpenAsync();
File.WriteAllBytes("targetFile.xyz", mySubclassedDoc.Content);
But if the file is larger (eg. 400MB) the app crashes before LoadFromContents is called because of insufficent memory (RAM).
So there need to be a way to stream the selected file directly to a file.
How can you do this using the given NSUrl ?
Since you have got the url, there's no need to convert it to NSData then store the data to a file. We can just use this url with NSFileManager like:
url.StartAccessingSecurityScopedResource();
string docPath = NSSearchPath.GetDirectories(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomain.User)[0];
string filePath = Path.Combine(docPath, "fileName.type");
NSError error;
NSFileManager fileManager = new NSFileManager();
fileManager.Copy(url.Path, filePath, out error);
url.StopAccessingSecurityScopedResource();
In this way we can store the file to our app's own document directory.
why don’t you just clone the file over to your Documents directory before reading it instead of deserializing the contents and reserializing them?
your code seems really inefficient. If your files can be 400MB you should clearly not load all the contents into memory. I guess you must have very large binary objects in the files if they can be 400MB ; try mmapping the file and storing pointers to the individual objects instead?

NSUrlSessionDownloadDelegate - DidFinishDownloading checking for "File Not Found"

I have setup a background downloader on my app for iOS. The only problem I am having is checking if the file exists server side (as it is possible that it may not).
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
This is always returning even if there is no file to grab but then will save the tmp file as if it has downloaded successfully. Like so.
// Create the filename
var documentsFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
NSUrl destinationURL = NSUrl.FromFilename(Path.Combine(documentsFolderPath, destFile));
// Remove any existing file in our destination
NSError error;
fileManager.Remove(destinationURL, out error);
bool success = fileManager.Copy(sourceFile, documentsFolderPath + "/" + destFile, out error);
if (!success)
{
Console.WriteLine("Error during the copy: {0}", error.LocalizedDescription);
}
Now how can I check the response status from here? As I am downloading in the background I cannot use a completion block and that is the only place I can find where I can possibly get the response status.
The only other way I can see to handle this is to "ping" the file before I setup a task to check it is there, is this correct or can I just allow this download delegate to handle it?

Save file in iCloud Drive Documents (user Access)

I want to save a PDF file in the iCloud Drive. The user should have access to the file over his iCloud Drive App.
At the moment I can save a file in the iCloud Drive but it is in a hidden directory.
struct DocumentsDirectory {
static let localDocumentsURL: NSURL? = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: .userDomainMask).last! as NSURL
static var url: NSURL? = FileManager.default.url(forUbiquityContainerIdentifier: nil)! as NSURL
static let iCloudDocumentsURL: NSURL? = url?.appendingPathComponent("Documents")! as! NSURL
}
With this code I get the hidden directory in the iCloud Drive (which is specific to my app).
Now my question is: can I save the file in the standard documents directory in the iCloud Drive? Or can I create a folder for the documents from my app, which the user can see?
Try saving the file under the 'Documents' directory, this ensure the file is saved under the users public directory and is visible in the iCloud app as well.
func setupiCloudDriveForFileExport() {
if let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent("Documents") {
if (!NSFileManager.defaultManager().fileExistsAtPath(iCloudDocumentsURL.path!, isDirectory: nil)) {
do {
try NSFileManager.defaultManager().createDirectoryAtURL(iCloudDocumentsURL, withIntermediateDirectories: true, attributes: nil)
}catch let error as NSError {
print(error)
}
}
}
}
This code checks for the existance of the Documents directory on iCloud and if not creates a new one.

How to save PDF files from Firebase Storage into App Documents for future use?

I have connected my App with the Firebase Storage where my 19ea PDF files exists.
I would like to download those files and save them locally for future use.
Those PDF files will be used inside UIWebviews but they may need to be updated in time. Therefore, I have configured version control system with Firebase Database, so I will be able to push the newer versions when I update the files in the storage.
So, how I can save those files locally? (to a folder like: user/myapp/Documents/PDF etc?)
Also, how I can check if that folder contains any documents and how to delete them before downloading new files?
Here is what I have got so far.
I appreciate all the help.
// Firebase Storage Connection
static var refStorage:FIRStorageReference?
static var dataPDF = [NSData]()
func newDataDownload(){
// Compare Current Data Version with Online Data Version
if myFirebaseData.localDataVersion < myFirebaseData.onlineDataVersion {
// Set Firebase Storage Reference
myFirebaseData.refStorage = FIRStorage.storage().reference()
for i in 1...myFirebaseData.onlineTotalPDFCount {
// Create a reference to the file you want to download
let pulledPDF = FIRStorage.storage().reference().child("/PDF/\(i).pdf")
// Create local filesystem URL
let localURL = URL(string: "myApp/Documents/PDF/\(i)")!
pulledPDF.data(withMaxSize: myFirebaseData.maxPDFdownloadSize, completion: { (downPDF, err) in
if err == nil {
// Accessed the data
myFirebaseData.dataPDF.append(downPDF! as NSData)
print(myFirebaseData.dataPDF)
} else {
// If there is an error print it
print(err.debugDescription)
}
})
}
}
// If Data is successfully downloaded update Local Data Version
myFirebaseData.localDataVersion = myFirebaseData.onlineDataVersion
Use storageRef.write(toFile: completion:) (docs), like:
// Create a reference to the file you want to download
let pdfRef = storageRef.child("files/file.pdf")
// Create local filesystem URL
let localURL = URL(string: "path/to/local/file.pdf")!
// Download to the local filesystem
let downloadTask = pdfRef.write(toFile: localURL) { url, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Local file URL for "path/to/local/file.pdf" is returned
}
}
Note that you can only write to /tmp and /Documents due to app sandboxing requirements (see Downloading Firebase Storage Files Device Issue for an example of how this fails otherwise).

How to clean nsurlsession downloadTask generate tmp File?

If I create a DownloadTask by nsurlsession, there was a tmp file named like 'CFNetworkDownload_1vY41L.tmp' in /Developer/tmp/ folder.
Then how to delete the tmp file when I delete downloadTask?
Moreover, I don't want to delete all tmp file because there are other downloadTask cache file.
Apple's documentation says that the file will be deleted once the download block finishes, check location explanation. And yes it is deleted, at least in iOS 12, you have to move it before it completes, no need to free space.
Example:
let task = self.session.downloadTask(with: request) { [weak self] url, response, error in
if let error = error {
...
}
guard let httpResponse = response as? HTTPURLResponse else {
fatalError("Couldn't get HTTP response")
}
if 200..<300 ~= httpResponse.statusCode, let downloadedPath = url {
// Move file in downloadedPath to a documents or other location
}
}
downloadPath will have the location of the file.
You can delete the file using removeItemAtPath:error: method of NSFileManager
if ([[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:#"%#%#", NSTemporaryDirectory(), #"Your File Name"] error:NULL])
{
NSLog(#"File deleted !!!");
}
else
{
NSLog(#"Couldn't delete the file !!!");
}

Resources