How to zip a folder using ZipArchive in iphone? - iphone

I have a folder in the documents directory that needs to be zipped.I was able to zip regular files but i was not able to zip folders.
I referred the following link
How to zip folders in iPhone SDK?
But here the files in the folder are zipped separately.I would like to zip the entire folder instead of having to deal with exploring the contents(files/folders) inside the directory and zipping them one by one.
Is it possible to do this with the ZipArchive library. If it is could some one please explain by posting the necessary code?
Thank You.

You can't do that with ZipArchive but have a look at SSZipArchive it has a + (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath;
method you could use.

// Path to store Zip
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* dPath = [paths objectAtIndex:0];
NSString* zipfile = [dPath stringByAppendingPathComponent:#"test.zip"] ;
// File Tobe Added in Zip
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"GetAllCardList" ofType:#"xml"];
NSString *fileName = #"MyFile"; // Your New ZipFile Name
ZipArchive* zip = [[ZipArchive alloc] init];
if([zip CreateZipFile2:zipfile])
{
NSLog(#"Zip File Created");
if([zip addFileToZip:filePath newname:[NSString stringWithFormat:#"%#.%#",fileName,[[filePath lastPathComponent] pathExtension]]])
{
NSLog(#"File Added to zip");
}
}

try SSZipArchive
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirector;
I used
let success = SSZipArchive.createZipFileAtPath(zipFullFilename, withContentsOfDirectory:dataDir, keepParentDirectory:true)
to create zip folder. It is worked.

You just need to get the contents of the dir prior to adding all the files to the zip as in the example. Adding them one by one is the same thing in code so just get a list then run through the list.
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:bundleRoot error:nil];
If you are looking for specific files you can also use a predicate to filter the results
NSPredicate *filter = [NSPredicate predicateWithFormat:#"self ENDSWITH '.png'"];
NSArray *pngs = [dirContents filteredArrayUsingPredicate:filter];

#implementation ZipArchive(Util)
+ (BOOL)makeZipFile:(NSString*)filepath withContentInDirectory:(NSString*)directory
{
ZipArchive* zip = [[ZipArchive alloc] initWithFileManager:[NSFileManager defaultManager]];
if ([zip CreateZipFile2:filepath]){
[self enumDirectory:directory zipFile:zip];
return [zip CloseZipFile2];
}
return NO;
}
+ (void)enumDirectory:(NSString*)directory zipFile:(ZipArchive*)zip
{
NSArray* resourceKeys = #[NSURLIsDirectoryKey];
NSDirectoryEnumerator<NSURL *> * enumerator = [[NSFileManager defaultManager] enumeratorAtURL:[NSURL fileURLWithPath:directory] includingPropertiesForKeys:resourceKeys options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:^BOOL(NSURL * _Nonnull url, NSError * _Nonnull error) {
return NO;
}];
for (NSURL* url in enumerator) {
NSDictionary<NSString *, id> *resourceValues = [url resourceValuesForKeys:resourceKeys error:nil];
BOOL isDirectory = [[resourceValues objectForKey:NSURLIsDirectoryKey] boolValue];
if (isDirectory) {
continue;
}
NSInteger len = [directory length];
NSString* subpath = [url.path substringFromIndex:len];
if ([subpath rangeOfString:#"/"].location == 0) {
subpath = [subpath substringFromIndex:1];
}
[zip addFileToZip:url.path newname:subpath];
}
}
#end

Related

Get only image files from directory

I am storing image file in cache directory . Later I want to get all image file list from cache directory. I am using following code to get all files.
[fileManager contentsOfDirectoryAtPath:pathForCacheDirectory error:&error]
How to separate image files from this. Image file can be any format.
Thanks in advance.
// Store your supported image Extensions
NSArray *extensionList = [NSArray arrayWithObjects:#"jpg", #"jpeg", #"png", #"gif", #"bmp", nil];
// Grab the content Directory
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pathForCacheDirectory error:&error];
NSMutableArray *listOfImageFiles = [NSMutableArray arrayWithCapacity:0];
// Check for Images of supported type
for(NSString *filepath in contents){
if ([extensionList containsObject:[filepath pathExtension]])
{
// Found Image File
[listOfImageFiles addObject:filepath];
}
}
NSLog(#"Lisf of Image Files : %#",listOfImageFiles);
a brutal way is to enum all extensions you consider it to be an image.
a better way is using UTI, check this Get the type of a file in Cocoa
You can filter file using extensions.
NSArray *contents = [fileManager contentsOfDirectoryAtPath:pathForCacheDirectory error:&error];
for(NSString *filepath in contents){
if ([[filepath pathExtension] isEqualToString: #"png"]) {
// Your code
}
}
Try this, hope this will help.
NSArray * contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:YOURPATH error:NULL];
NSMutableArray * onlyImages = [[NSMutableArray alloc]init];
for (NSString * contentPath in contents) {
NSString * lastPath = [contentPath pathExtension];
if ([lastPath isEqualToString:#"jpg"] || [lastPath isEqualToString:#"jpeg"] || [lastPath isEqualToString:#"png"] || /* any other */ ) {
[onlyImages addObject:contentPath]; // only images
}
}
See this & check:
CFStringRef fileExtension = (CFStringRef) [file pathExtension];
CFStringRef fileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
if (UTTypeConformsTo(fileUTI, kUTTypeImage)) NSLog(#"It's an image");
else if (UTTypeConformsTo(fileUTI, kUTTypeMovie)) NSLog(#"It's a movie");
else if (UTTypeConformsTo(fileUTI, kUTTypeText)) NSLog(#"It's text");
else NSLog(#"It's audio");

unable to omit #".DS_Store" file while fetching contents of Document Drectory

I am working on storing a list of audio files into my document directory and then fetching them.
It gives me a list of audio files along with this it gives me a file with name #".DS_Store". While fetching content I want to leave this file of documents directory.
Is there any way I can get rid of this while fetching the audio list other than removing this from array or apply a #".DS_Store" check.
What exactly is the reason for this.?
#pragma mark - Saving Audio in Document Directory
-(void)saveAudioinDocumentDirectory:(ASIHTTPRequest *)theRequest
{
/*save the Audio file in Document Directory */
NSFileManager *fileManager=[NSFileManager defaultManager];
NSLog(#"GOT THE SIZe OF AUDIO %d",[[theRequest responseData] length]);
NSLog(#"AUDIO ID IS %#",[[theRequest userInfo] valueForKey:#"audioIndex"]);
/*Get the Path to Application Documents Directory*/
NSArray *docDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
/*append the neccessary file extension */
NSString *filepathStr=[NSString stringWithFormat:#"/%#/%#.mp3",docDir,[NSString stringWithFormat:#"%#",[[theRequest userInfo] valueForKey:#"audioIndex"]]];
/*Check if my crrent file exists in the Documents Directory*/
if(![fileManager fileExistsAtPath:filepathStr])
{
/* file doesnt exists */
/*create a NSdata of File*/
NSData *data=[NSData dataWithData:[theRequest responseData]];
NSLog(#"%#",filepathStr);
if ([data length] >0 ){
/*write the File at the Location in Documents Directory */
[data writeToFile:filepathStr atomically:YES];
NSLog(#"Successfully saved the file to %#", filepathStr);
}
else if([data length] == 0)
{
NSLog(#"Nothing was downloaded.");
}
}
/*After saving fetch the path til documents Directory*/
NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
/*Get the Path for Files */
NSString *s=[folders objectAtIndex:0];
/*Fetch the list of Files stored in Documents Directory*/
NSArray *contents = [fileManager contentsOfDirectoryAtPath:s error:NULL];
NSLog(#"TOTAL NUMBER OF AUDIO FILES %d %#",[contents count],[contents objectAtIndex:0]);
if([Audiolistforstr isEqualToString:#"AudioListForIntro"])
{
// NSLog(#"Audiolistforstr#"IntroThirdRow" in reading audio from document Intro IS %#",Audiolistforstr);
/*Intro*/
[AudioListArrForIntro removeAllObjects];
[AudioListArrForIntro addObjectsFromArray:contents];
if([AudioListArrForIntro containsObject:#".DS_Store"])
{
[AudioListArrForIntro removeObject:#".DS_Store"];
}
NSLog(#"FINAL LIST %#",AudioListArrForIntro);
}
else if([Audiolistforstr isEqualToString:#"AudioListForCredits"])
{
// NSLog(#"Audiolistforstr#"IntroThirdRow" in reading audio from document credit IS %#",Audiolistforstr);
/*credits*/
[AudioListArrForCredits removeAllObjects];
[AudioListArrForCredits addObjectsFromArray:contents];
if([AudioListArrForCredits containsObject:#".DS_Store"])
{
[AudioListArrForCredits removeObject:#".DS_Store"];
}
NSLog(#"FINAL LIST %#",AudioListArrForCredits);
}
/* Did we find anything? */
if([Audiolistforstr isEqualToString:#"AudioListForIntro"])
{
// NSLog(#"Audiolistforstr#"IntroThirdRow" in reading audio fromRELOADNG TABLE Intro IS %#",Audiolistforstr);
/*Intro*/
if ([AudioListArrForIntro count] == 0)
{
}
else
{
UIView *vw=(UIView *)[self.view viewWithTag:ViewAddAudioIntroTag];
[(UITableView *)[vw viewWithTag:tblIntroAudioListTag] reloadData];
}
}
else if([Audiolistforstr isEqualToString:#"AudioListForCredits"])
{
// NSLog(#"Audiolistforstr#"IntroThirdRow" in reading audio fromRELOADNG TABLE Intro IS %#",Audiolistforstr);
/*Credits*/
if ([AudioListArrForCredits count] == 0)
{
}
else
{
/*AudioListForCredits*/
UIView *vw=(UIView *)[self.view viewWithTag:ViewAddAudioCreditsTag];
[(UITableView *)[vw viewWithTag:tblCreditsAudioListTag] reloadData];
}
}
}
Any help would be appreciated.
Thanks
Vikas
You can check for .DS_Store after NSArray *docDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; line........that is,
NSMutableArray * dirContents = [[NSMutableArray alloc] initWithArray:docDir];
if([docDir containsObject:#".DS_Store"])
{
[dirContents removeObject:#".DS_Store"];
}
By this, dirContents removes the entry of .DS_Store.
Filter your document directory contents. For example, if you are having audio files with extension of .mp3, then you can get all the mp3 files as below:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:NULL];
directoryContent = [directoryContent filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"pathExtension ==[c] %#", #"mp3"]];
This will omit all other files than the mp3 files..
All the best!!!
The API you're using:
NSArray *contents = [fileManager contentsOfDirectoryAtPath:s error:NULL];
returns all files found at the path, which would include the ".DS_Store" file.
I'd recommend assigning "contents" to a mutable array, e.g.:
NSMutableArray * contents =
[[NSMutableArray alloc] initWithArray: [fileManager contentsOfDirectoryAtPath:s error:NULL]];`
and iterate through the array to find and removing any and all files that don't have ".mp3" as a path extension.
I'd also recommend not starting any variable with an upper case letter (e.g. instead of "Audiolistforstr", use "audiolistforstr" or even better, "arrayofAudioFiles"). Objective C best practice is to start all variables and methods with lower case letters.
Its Working well..
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *imageFilenames = [manager contentsOfDirectoryAtPath:documentsDirectory error:nil];
for (int i = 0; i < [imageFilenames count]; i++)
{
NSString *imageName = [NSString stringWithFormat:#"%#/%#",documentsDirectory,[imageFilenames objectAtIndex:i] ];
if (![[imageFilenames objectAtIndex:i]isEqualToString:#".DS_Store"])
{
UIImage *myimage = [UIImage imageWithContentsOfFile:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:_myimage];
}
}

Zip/UnZip File on Webserver using our iphone

Is it Possible to Zip/UnZip File on Webserver using our iphone to use that file ?
If it is, please guide me how can I do that? My Problem is I want to upload/Download a Folder from webserver to local and I do that by first zip local folder and then upload it from webserver..
Is it right way to Upload/Download folder by Creating Zip?
-(void)updateFromInternet
{
progressView.progress = 0;
//save to a temp file
NSString* updateURL = #"http://www.touch-code-magazine.com/wp-content/uploads/2010/06/LargeAppUpdate.zip";
NSLog(#"Checking update at : %#", updateURL);
progressView.hidden = NO;
responseData = [[NSMutableData alloc] init];
NSURLRequest* updateRequest = [NSURLRequest requestWithURL: [NSURL URLWithString:updateURL]];
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:updateRequest delegate:self];
[connection start];
NSLog(#"Download start...");
}
if u have any query check this link
http://www.touch-code-magazine.com/update-dynamically-your-iphone-app-with-new-content/
Finally Its Over.
Using RecursionFunction this will Complete... No need to Zip/Unzip folder..
-(void)recursiveMethod:(NSString *)strPath
{
[client1 initWithHost:#"HostName" username:self.username password:self.password];
NSArray *listFiles = [[NSArray alloc]init];
//Fetching data from document directory.
// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//
// // Get documents folder
// NSString *docDir = [paths objectAtIndex:0];
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:strPath isDirectory:&isDir] && isDir)
{
subpaths = [fm subpathsAtPath:strPath];
}
listFiles = [fm contentsOfDirectoryAtPath:strPath error:nil];
NSLog(#"listFiles ==== %#",listFiles);
for(int j=0; j < [listFiles count] ;j++)
{
//Check For Folder
if([[listFiles objectAtIndex:j]rangeOfString:#"."].location == NSNotFound)
{
self.folderName = [self.folderName stringByAppendingPathComponent:[listFiles objectAtIndex:j]];
//Create Current Folder
[client1 createCollection:self.folderName];
self.countItemsInFolder = 0; //countItemsInFolder is int(counter)
//call recursiveMethod again
strPath = [strPath stringByAppendingPathComponent:[listFiles objectAtIndex:j]];
[self recursiveMethod:strPath];
self.folderName = [self.folderName stringByDeletingLastPathComponent];
strPath = [strPath stringByDeletingLastPathComponent];
}
else
{
//upload Files
self.countItemsInFolder = j+1;
[self appdelegate].remoteFolderForUpload = self.folderName;
NSString *destiFilePath = [strPath stringByAppendingPathComponent:[listFiles objectAtIndex:j]];
[client1 uploadFile:[listFiles objectAtIndex:j] toPath:self.folderName fromPath:destiFilePath];
}
}
if([listFiles count] == self.countItemsInFolder)
{
strPath = [strPath stringByDeletingLastPathComponent];
}
}
Download
- download zip from server and unzip it.
Upload
- Zip the files on device using miniZip and upload to server
Unzip the .zip on server. Use a script to do this automatically. Not necessarily to remote control the unzip process on server through iPhone.

App rejected because of Data Storage - how do I get it to ignore all of my files in a folder?

I have an app that downloads a ton of photos and stores them in a subfolder of the Documents folder which was apparently fine until iOS 5.1
Now Apple is telling me I need to store them else where or somehow mark them as not for backup. This is an app update so for most of my users the data will already exist in these subfolders.
How do I get iOS to skip all of the files in my subfolders of Documents or to skip a particular file in the Documents folder?
It would be a HUGE undertaking to move all of the files to the cache like they suggest.
I read this but I am no sure exactly where I am suppose to implement this:
https://developer.apple.com/library/ios/#qa/qa1719/_index.html
You can use NSFileNanager to list all the files, then call the function that is suggested in your like. Your code would be something like:
// From Apple FAQ
#import <sys/xattr.h>
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
- (void) addSkipBackupAttributeToItemsInFolder:(NSString*)folder
{
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:folder error:nil];
for (int curFileIdx = 0; curFileIdx < [dirContents count]; ++curFileIdx)
{
NSString* curString = [folder stringByAppendingPathComponent:[dirContents objectAtIndex:curFileIdx]];
NSURL* curFileUrl = [NSURL fileURLWithPath:curString];
[self addSkipBackupAttributeToItemAtURL: curFileUrl];
}
}
And you will use this like that:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
[self addSkipBackupAttributeToItemsInFolder:documentsDirectory];

How do I get all resource paths in my bundle recursively in iOS?

I have an "unzipped" folder in my application bundle. I need to get the resource paths for all files of type txt. I've been using this,
NSArray *filePaths = [NSBundle pathsForResourcesOfType:#"txt" inDirectory:[[[NSBundle mainBundle] bundlePath] stringByAppendingString:#"/unzipped"]];
But it only gets files in the first directory. Is there a recursive version of this somewhere that I'm just missing?
I got this working using the code posted by #rekle as a starting point. The trick is to use NSDirectoryEnumerator, which will do this recursively. Here's the function I wrote in case anyone needs it.
- (NSArray *)recursivePathsForResourcesOfType:(NSString *)type inDirectory:(NSString *)directoryPath{
NSMutableArray *filePaths = [[NSMutableArray alloc] init];
// Enumerators are recursive
NSDirectoryEnumerator *enumerator = [[[NSFileManager defaultManager] enumeratorAtPath:directoryPath] retain];
NSString *filePath;
while ((filePath = [enumerator nextObject]) != nil){
// If we have the right type of file, add it to the list
// Make sure to prepend the directory path
if([[filePath pathExtension] isEqualToString:type]){
[filePaths addObject:[directoryPath stringByAppendingPathComponent:filePath]];
}
}
[enumerator release];
return [filePaths autorelease];
}
Swift, using NSURL
func recursivePathsForResources(type type: String) -> [NSURL] {
// Enumerators are recursive
let enumerator = NSFileManager.defaultManager().enumeratorAtPath(bundlePath)
var filePaths = [NSURL]()
while let filePath = enumerator?.nextObject() as? String {
if NSURL(fileURLWithPath: filePath).pathExtension == type {
filePaths.append(bundleURL.URLByAppendingPathComponent(filePath))
}
}
return filePaths
}
Just an update to this method to allow any file types (nil) and provide the correct path format
- (NSArray *)recursivePathsForResourcesOfType:(NSString *)type inDirectory:(NSString *)directoryPath{
NSMutableArray *filePaths = [[NSMutableArray alloc] init];
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:directoryPath];
NSString *filePath;
while ((filePath = [enumerator nextObject]) != nil){
if (!type || [[filePath pathExtension] isEqualToString:type]){
[filePaths addObject:[directoryPath stringByAppendingPathComponent:filePath]];
}
}
return filePaths;
}
Try something like this:
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSError *error = nil;
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePath error:&error];
'files' will be an array of strings containing all the filenames in that path...
Swift 3.1 version of the answer from smokinggoyster
func recursivePathsForResources(type: String, in directoryPath: String) -> [String] {
// Enumerators are recursive
let enumerator = FileManager.default.enumerator(atPath: directoryPath)
var filePaths: [String] = []
while let filePath = enumerator?.nextObject() as? String {
if URL(fileURLWithPath: filePath).pathExtension == type {
filePaths.append(directoryPath.byAppending(pathComponent: filePath))
}
}
return filePaths
}
Before copying think about create symlink. You can create just one symlink to folder with a lot of items.
And it will work in most situations.
You will be free from creating another thread, waiting for end of operation, etc.