how to zip empty folders in File manager in iOS? - ios

I am developing a app like file manager. In this i have created zip folder using any existing folder, it works perfectly fine. For this i have used SSZipArchive API. After zip i send it through mail. It's working fine too.
Now when i try to zip any empty folder, it is not working. Means empty folder does not zip.

Yeah looking at the source code, the method + createZipFileAtPath:withContentsOfDirectory: only appears to care about files:
while ((fileName = [dirEnumerator nextObject])) {
BOOL isDir;
NSString *fullFilePath = [directoryPath stringByAppendingPathComponent:fileName];
[fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir];
if (!isDir) {
[zipArchive writeFileAtPath:fullFilePath withFileName:fileName];
}
}
You have two choices:
Fix it yourself and then send a pull request to the author on github and contribute to work you have, yourself, benefited from.
Live with it. It's not clear how an empty folder not appearing in the zip file is an issue anyway.

Isn't a standard thing to have stub files in directories like that. Some file with no contents.

Related

Dropbox API v2 for uploading media files in iOS

I am trying to transition from Dropbox API v1 to v2. My objective is to upload video files to Dropbox in the app folder that Dropbox creates for the apps that do not require access to root folder. I checked this tutorial but have the following confusions:
NSData *fileData = [#"file data example" dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
// For overriding on upload
DBFILESWriteMode *mode = [[DBFILESWriteMode alloc] initWithOverwrite];
[[[client.filesRoutes uploadData:#"/test/path/in/Dropbox/account/my_output.txt"
mode:mode
autorename:#(YES)
clientModified:nil
mute:#(NO)
inputData:fileData]
setResponseBlock:^(DBFILESFileMetadata *result, DBFILESUploadError *routeError, DBRequestError *networkError) {
if (result) {
NSLog(#"%#\n", result);
} else {
NSLog(#"%#\n%#\n", routeError, networkError);
}
}] setProgressBlock:^(int64_t bytesUploaded, int64_t totalBytesUploaded, int64_t totalBytesExpectedToUploaded) {
NSLog(#"\n%lld\n%lld\n%lld\n", bytesUploaded, totalBytesUploaded, totalBytesExpectedToUploaded);
}];
What should be "/test/path/in/Dropbox/account/my_output.txt" in my case, as I do not access the root folder?
Whether the same code is supposed to work for binary files such as mp4 files (it uses UTF8 encoding in the sample code when preparing NSData)?
The "/test/path/in/Dropbox/account/my_output.txt" in the sample is just an example. You should supply the path for the desired location of the uploaded file in the Dropbox account. If you're using an app folder app, the root you supply will automatically be translated into the app folder itself. For example, if you have an app folder at "/Apps/MyAppName", and you want to upload a file named "video.mp4" into a folder called "Videos" in your app folder, you should supply a path value of "/Videos/video.mp4". That will automatically become /Apps/MyAppName/Videos/video.mp4 in the account.
The sample makes an NSData by encoding a string, but you can use the same uploadData to upload a file from any NSData.
Basically this path is /test/path/in/Dropbox/account/my_output.txt.
In dropbox account it will create folders like this test>path>in>Dropbox>account-- then your file will be in account folder. You can replace it with
/yourFolderName/Yourfilename.extension

Access any file inside Zip File [duplicate]

I'm trying to port an existing Android application to iOS,
In the Android application i was using a ZipInputStream to extract a single file from the zip archive and store it in as a temporary file.
How could i extract a single file from a Zip Archive without having to extract the whole archive (As it is very large)?
I just found the answer,
I had to modify SSZipArchive to insert a method that will extract a single file from the ZIP archive (Based on its name)
You could find the modified version here, if someone finds this useful i might clean it up, add the delegate, the tests and propose for pull in SSZipArchive.
Usage is straightforward:
NSString *zipEntityToExtract = #"example.aac";
NSString *destinationFilePath = ...; //(Includes filename)
NSString *zipPath = ...;
[SSZipArchive unzipEntityName:zipEntityToExtract fromFilePath:zipPath toDestination:filePath];
//File is now in destinationFilePath

Large files downloaded to Documents and backed up to iCloud

I have an iOS app in the app store that can download relatively large files that need to stay on the device for offline use. Those files are currently stored in the app's Documents folder but I'm just now reading that the Documents folder is backed up and should really only be used for user-generated content. This Apple technical Q&A states that the NSURLIsExcludedFromBackupKey should be set to prevent backup. This states that an app's /Library/Caches is the right place to put these kinds of files although further reading suggests that the folder may be cleared when the device is low on storage which is unacceptable for this app. I believe /Library/Application Support/ is then the best location for them -- does this sound right?
Unfortunately, this mistake got through the app review process. What are some best practices for fixing this now that people are using the app and already have some files persisted to the Documents folder and to their backups? It seems I need to move all the existing files and set their NSURLIsExcludedFromBackupKey on app update. How do I guarantee that this is done exactly once and that it isn't interrupted? Is moving the files out of the Documents folder important or could I leave them there? Will changing the files' backup status remove them from existing backups?
I'm using Swift 2.1.1 and targeting iOS 8.0+.
As stated in the technical Q&A, you best bet could be create a subdirectory in the Documents, and exclude that subdirectory once.
I don't believe you can write a 'do it once and be sure it is done' routine, since you can't guarantee your app doesn't crash while it is running. You certainly could set a completion flag when you are sure it is done so that once it is done you don't have to run it again.
Exclude your directory from backup, not the individual files.
From Xcode:
You can use this property to exclude cache and other app support files which are not needed in a backup. Some operations commonly made to user documents cause this property to be reset to false; consequently, do not use this property on user documents.
Here is the strategy I have used with good results
(sorry, its in objective-c -- I'm not a swift guy. Hopefully it will give you the idea):
- (BOOL)moveAllFiles{
// Searches through the documents directory for all files ending in .data
BOOL success = true;
NSString *myNewLocation = #"/store/my/files/here/now";
// Get the documents directory
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories objectAtIndex:0];
// Get all files ending in .data (whatever your file type is)
NSArray *dataFilesArray = [NSArray arrayWithArray:[NSBundle pathsForResourcesOfType:#"data" inDirectory:documentDirectory]];
// If you have multiple resource types, use this line once each for each resource type, then append the arrays.
// Iterate the found files
NSString *fileName = [NSString string];
for (int i=0; i<[dataFilesArray count]; i++) {
fileName = [[dataFilesArray objectAtIndex:i] lastPathComponent];
// Move file, set success to false if unsuccessful move
if (![[NSFileManager defaultManager] moveItemAtPath:[dataFilesArray objectAtIndex:i]
toPath:[myNewLocation stringByAppendingPathComponent:fileName]
error:nil]) {
success = false; // Something went wrong
}
}
return success;
}
Now use the value of success to set a key in the user defaults file. Check for that key on startup. If it is false (or absent), run this routine (again).
This example is with file paths. You can do the same thing with file URLs if you wish.

Unzipping a single file from Archive

I'm trying to port an existing Android application to iOS,
In the Android application i was using a ZipInputStream to extract a single file from the zip archive and store it in as a temporary file.
How could i extract a single file from a Zip Archive without having to extract the whole archive (As it is very large)?
I just found the answer,
I had to modify SSZipArchive to insert a method that will extract a single file from the ZIP archive (Based on its name)
You could find the modified version here, if someone finds this useful i might clean it up, add the delegate, the tests and propose for pull in SSZipArchive.
Usage is straightforward:
NSString *zipEntityToExtract = #"example.aac";
NSString *destinationFilePath = ...; //(Includes filename)
NSString *zipPath = ...;
[SSZipArchive unzipEntityName:zipEntityToExtract fromFilePath:zipPath toDestination:filePath];
//File is now in destinationFilePath

Invalidating QLPreviewController "cache"

QLPreviewController seems to cache file contents based on the local file's URL. In my application, the file contents can be updated remotely and would cause the new contents to be downloaded.
If I view a file in QLPreviewController, update it remotely, then re-preview it, the file does not show up as updated.
The file is definitely updated on disk, and other controls show the correct updated file.
The workaround I'm using right now is to basically move a file when it's previewed to a unique filename (using timestamp), which will of course not be in the QLPreviewController's cache. However, this has other repercussions, for example, if the app is killed or it crashes (god forbid), I won't know "where" to find the downloaded file.
I'm looking for less invasive hacks, or solutions to making QLPreviewController refresh its cache. The APIs don't seem to expose anything, so don't be afraid to submit a hack if it's less gross than the one I've presented above (not including copying/moving the file to a guaranteed unique URL, which I am already utilizing).
Just ran into this issue myself. I solved it by recreating the QLPreviewController each time I reload an item with the same name as the currently viewed item. Creating a new QLPreviewController clears the cache.
I know this is an old question but someone might have the same problem and find this answer helpful.
You should use refreshCurrentPreviewItem after downloading complete
I had the same problem. Opening a locally generated CSV file.
I have my _previewController* setup as a #property of my controller. Then what i did:
- (void)viewDidLoad
{
[super viewDidLoad];
self.previewController = [[QLPreviewController alloc] init];
_previewController.delegate=self;
_previewController.dataSource=self;
}
- (void)previewCSV
{
[_previewController reloadData]; // this triggers a reload
[self presentModalViewController:_previewController animated:YES];
}
IN other solution that comes to mind (not tested).
Depending on your URL, you could add something like http://url?time=123456 to your URL. Like this you change the URL but without side effect. The time (or any other parameter) you can change on each request.
It's the ugliest bug in iOS. Cache management in iOS 5 and beyond. I think is the same reason that makes iCloud buggy, Share-at-Home crashing and so on. Bad cache managements and so worst synchronization systems.
Well, my solution for this was to store the download file in a folder and use the current date to name the folder. It is equivalent to #Rogier's solution, but this works always. You get a name for the folder, for example, with [[NSDate date] description]. Instead of saving the file replacing the old one, you delete previous file, delete previous folder and save new file in a new folder. It's working fine for me.
Just remove all files from tmp directory like this:
- (void)clearCache
{
NSString *tempPath = NSTemporaryDirectory();
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tempPath error:nil];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (int i = 0; i < [dirContents count]; i++) {
NSLog(#"Directory Count: %i", [dirContents count]);
NSString *contentsOnly = [NSString stringWithFormat:#"%#%#", tempPath, [dirContents objectAtIndex:i]];
[fileManager removeItemAtPath:contentsOnly error:nil];
}
}

Resources