Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a ZIP file from a string in Swift

 let data = "InPractiseThisWillBeAReheallyLongString"
     
        createDir()
        
        let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let ourDir = docsDir.appendingPathComponent("ourCustomDir/")
        let tempDir = ourDir.appendingPathComponent("temp/")
        let unzippedDir = tempDir.appendingPathComponent("unzippedDir/")
        let unzippedfileDir = unzippedDir.appendingPathComponent("unZipped.txt")
        let zippedDir = tempDir.appendingPathComponent("Zipped.zip")
        do {
            
            try data.write(to: unzippedfileDir, atomically: false, encoding: .utf8)
            
            
            let x = SSZipArchive.createZipFile(atPath: zippedDir.path, withContentsOfDirectory: unzippedfileDir.path)
            
            var zipData: NSData! = NSData()
            
            do {
                zipData = try NSData(contentsOfFile: unzippedfileDir.path, options: NSData.ReadingOptions.mappedIfSafe)
                //once I get a readable .zip file, I will be using this zipData in a multipart webservice
            }
            catch let err as NSError {
                print("err 1 here is :\(err.localizedDescription)")
            }
        }
        catch let err as NSError {
            
            print("err 3 here is :\(err.localizedDescription)")
        }

And the createDir function is:

func createDir() {
        let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let ourDir = docsDir.appendingPathComponent("ourCustomDir/")
        let tempDir = ourDir.appendingPathComponent("temp/")
        let unzippedDir = tempDir.appendingPathComponent("unzippedDir/")
        let fileManager = FileManager.default
        if fileManager.fileExists(atPath: tempDir.path) {
            deleteFile(path: tempDir)
            deleteFile(path: unzippedDir)
        } else {
            print("file does not exist")
            do {
                try FileManager.default.createDirectory(atPath: tempDir.path, withIntermediateDirectories: true, attributes: nil)
                try FileManager.default.createDirectory(atPath: unzippedDir.path, withIntermediateDirectories: true, attributes: nil)
                print("creating dir \(tempDir)")
            } catch let error as NSError {
                print("here : " + error.localizedDescription)
            }
        }
    }

Right now I am not getting any errors but when I download my appData container, get the ZIP file and attempt to unzip, I ma told the ZIP file is empty. I can see that the unzipped.text file does exist as expected.

Any idea what I'm doing wrong?

Is there a method to create a .zip directly from the string without having to save the file to the data container?


UPDATE

I also tried the following and have the exact same results:

let zipArch = SSZipArchive(path: zippedDir.path)
        print(zipArch.open)
        print(zipArch.write(dataStr.data(using: String.Encoding.utf8)!, filename: "blah.txt", withPassword: ""))
        print(zipArch.close)
like image 463
user2363025 Avatar asked Oct 18 '17 15:10

user2363025


Video Answer


3 Answers

I did it with ZipFoundation --> https://github.com/weichsel/ZIPFoundation

SWIFT

100% Working!!

1) Add ZipFoundation Framework to your project

2) Add import to your class

   import ZIPFoundation //Add to top of your class

Call Function

     zipString() . // Do your work
     extract(fileName: "myZip.zip") //Extract it to test it
     read(fileName : "zipTxt.txt" )  //Read you extracted File

Add function to your class

func zipString() {

    let zipName = "myZip.zip"
    let myTxtName = "zipTxt.txt"
    let myString = "I love to zip files"  // In your case "InPractiseThisWillBeAReheallyLongString"

    if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
        let fileURL = dir.appendingPathComponent(zipName)

        //Create the zip file
        guard let archive = Archive(url: fileURL, accessMode: .create) else  {
            return
        }

       /* guard let archive = Archive(url: fileURL, accessMode: .update) else  {
            return
        } */ // In case you want to update

        print("CREATED")

         //Add file to
        let data = myString.data(using: .utf8)
        try? archive.addEntry(with: myTxtName, type: .file, uncompressedSize: UInt32(data!.count), compressionMethod: .deflate, provider: { (position, size) -> Data in

            return data!
        })

         print("ADDED")
    }

}

Bonus File Extraction

    /**
 ** Extract file for a given name
 **/
func extract(fileName : String){

    let fileManager = FileManager()
    let file = fileName
    // let currentWorkingPath = fileManager.currentDirectoryPath
    if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {

        let fileURL = dir.appendingPathComponent(file)
        do {
            // try fileManager.createDirectory(at: dir, withIntermediateDirectories: true, attributes: nil)
            try fileManager.unzipItem(at: fileURL, to: dir)
            print("EXTRACTED")

        } catch {
            print("Extraction of ZIP archive failed with error:\(error)")
        }

    }

}

For testing your extracted file

      func read(fileName : String){
    let file = fileName //this is the file. we will write to and read from it

    if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {

        let fileURL = dir.appendingPathComponent(file)

        //reading
        do {
            let text2 = try String(contentsOf: fileURL, encoding: .utf8)
            print(text2)
        }
        catch {/* error handling here */}
    }
}

My Console Output...

CREATED

ADDED

EXTRACTED

I love to zip files

like image 151
user3826696 Avatar answered Oct 18 '22 21:10

user3826696


Little note: You can zip anything without using third party libraries ;)

/// Zip the itemAtURL (file or folder) into the destinationFolderURL with the given zipName
/// - Parameters:
///   - itemURL: File or folder to zip
///   - destinationFolderURL: destination folder
///   - zipName: zip file name
/// - Throws: Error in case of failure in generating or moving the zip
func zip(itemAtURL itemURL: URL, in destinationFolderURL: URL, zipName: String) throws {
    var error: NSError?
    var internalError: NSError?
    NSFileCoordinator().coordinate(readingItemAt: itemURL, options: [.forUploading], error: &error) { (zipUrl) in
        // zipUrl points to the zip file created by the coordinator
        // zipUrl is valid only until the end of this block, so we move the file to a temporary folder
        let finalUrl = destinationFolderURL.appendingPathComponent(zipName)
        do {
            try FileManager.default.moveItem(at: zipUrl, to: finalUrl)
        } catch let localError {
            internalError = localError as NSError
        }
    }
    
    if let error = error {
        throw error
    }
    if let internalError = internalError {
        throw internalError
    }
}
like image 21
Kappe Avatar answered Oct 18 '22 22:10

Kappe


You could use ZIPFoundation, which is another Swift ZIP library that allows you to read, create and modify ZIP files. One of its advantages is that it allows you to add ZIP entries "on-the-fly". You don't have to write your string to disk before creating an archive from it. It provides a closure based API where you can feed the string directly into a newly created archive:

func zipString() {
    let string = "InPractiseThisWillBeAReheallyLongString"
    var archiveURL = URL(fileURLWithPath: NSTemporaryDirectory())
    archiveURL.appendPathComponent(ProcessInfo.processInfo.globallyUniqueString)
    archiveURL.appendPathExtension("zip")
    guard let data = string.data(using: .utf8) else { return }
    guard let archive = Archive(url: archiveURL, accessMode: .create) else { return }

    try? archive.addEntry(with: "unZipped.txt", type: .file, uncompressedSize: UInt32(data.count), provider: { (position, size) -> Data in
        return data
    })
}

The addEntry method also has an optional bufferSize parameter that can be used to perform chunked addition (so that you don't have to load the whole data object into RAM.)

like image 26
Thomas Zoechling Avatar answered Oct 18 '22 21:10

Thomas Zoechling