Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unzipping in-memory archive #173

Open
MaxDesiatov opened this issue Jun 5, 2020 · 7 comments · May be fixed by #160
Open

Unzipping in-memory archive #173

MaxDesiatov opened this issue Jun 5, 2020 · 7 comments · May be fixed by #160

Comments

@MaxDesiatov
Copy link

Is your feature request related to a problem? Please describe.
It doesn't seem like there's a way to unzip an in-memory archive created with Archive(data:) initializer.

Describe the solution you'd like
An extension function on FileManager that takes an instance of Archive as an argument and unzips the archive in a given directory.

Describe alternatives you've considered
I've tried writing an in-memory archive to the filesystem first and then using the existing unzipItem extension function on FileManager, but that has a performance hit due to the fact that this requires writing the archive itself to disk.

Additional context
Writing the archive to disk is not needed in my case, it's held entirely in memory as it was donwloaded from network and there's no need to write the archive itself to disk, the app needs to write unzipped archive content directly to disk. The in-memory zip data is immediately discarded.

@weichsel
Copy link
Owner

Hi Max,

Sorry for the delayed reply.

There are currently no convenience methods on FileManager to extract in-memory archives. There is a pending PR that implements this though. I will need to find some time to review it and fix some minor issues before we can merge this into master - but at a first glance the PR additions look good. So you can maybe use the PR branch or copy the unzipItem code into a category.

@WowbaggersLiquidLunch
Copy link

A workaround I'm currently using is extracting each entry individually in an archive. The destination could be either on disk or in memory. This is also how zipping and unzipping an entire archive works internally in ZIP Foundation.

@danio0701
Copy link

A workaround I'm currently using is extracting each entry individually in an archive. The destination could be either on disk or in memory. This is also how zipping and unzipping an entire archive works internally in ZIP Foundation.

Can u tell me how u did it? I struggle to unpack the password protected archive to memory.

@Yourney
Copy link

Yourney commented Mar 9, 2023

This is really awesome ... in-memory unzipping!
I wrote a small app to test this, using cocoapods to import the dependency.
It is unclear to me how I can get the unzipped file. Perhaps somebody can explain that?

I would expect I have to do something like this:

        // data contains the zipped data
        let archive = ZIPFoundation.Archive(data: data, accessMode: .read)
        
        for entry in archive! {
            print(entry.path)  // Here I succesfully print the paths of the zipped components inside the zipfile
            if entry.type == .file,
               let data = entry.dataDescriptor?.data {
                
            }
        }

But that gives me an error: "'dataDescriptor' is inaccessible due to 'internal' protection level"

How do I get the unzipped data for an entry?
Or should I not look at the entries?

@Yourney
Copy link

Yourney commented Mar 13, 2023

I solved it:

        if let archive = ZIPFoundation.Archive(data: data, accessMode: .read) {
            for entry in archive {
                do {
                    if entry.path == "myfile.text" {
                        var txtData = Data()
                        _ = try archive.extract(entry) { data in
                            txtData.append(data)
                        }
                        // here we have our unzipped text (in txtData)!!
                    }
                } catch {
                    print(error)
                }
            }
        }

Awesome!

@WowbaggersLiquidLunch
Copy link

@danio0701 Sorry for this really really late response!

The way I did it (here in this semi-abandoned project) is mostly the same as how @Yourney did it. The only thing I want to mention is that, depending on whether you want the entire directory structure, or just the files, or any specific file, you can guard against the rest or supply a where clause to the loop:

guard let archive = Archive(data: yourData, accessMode: .read) else { ... }

for entry in archive where entry.type == .file /* or .directory depending on your need */ {
    do {
        _ = try archive.extract(entry, skipCRC32: true, progress: nil) { data in
            // do what you need with the data
            // e.g. reassemble the directory structure here if you need it
        }
    } catch { ... }
}

@Yourney
Copy link

Yourney commented Mar 13, 2023

Thanks for the added tips!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants