Monday, September 10, 2012

Folder zip/un-zip in Metro c#


using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;

namespace FileShare
{
    class FolderZip
    {
        /// <summary>
        /// ZipFolder and its sub folders
        /// </summary>
        /// <param name="tcpSocket"></param>
        async private static Task ZipFolderContents(StorageFolder sourceFolder, ZipArchive archive, string baseDirPath)
        {
            IReadOnlyList<StorageFile> files = await sourceFolder.GetFilesAsync();

            foreach (StorageFile file in files)
            {
                ZipArchiveEntry readmeEntry = archive.CreateEntry(GetCompressedFileName(baseDirPath, file));

                byte[] buffer = WindowsRuntimeBufferExtensions.ToArray(await FileIO.ReadBufferAsync(file));

                // And write the contents to it
                using (Stream entryStream = readmeEntry.Open())
                {
                    await entryStream.WriteAsync(buffer, 0, buffer.Length);
                }
            }

            IReadOnlyList<StorageFolder> subFolders = await sourceFolder.GetFoldersAsync();

            if (subFolders.Count() == 0) return;

            foreach (StorageFolder subfolder in subFolders)
                await ZipFolderContents(subfolder, archive, baseDirPath);
        }

        async public static Task ZipFolder(StorageFolder sourceFolder,  StorageFolder destnFolder,  string zipFileName)
        {
            StorageFile zipFile = await destnFolder.CreateFileAsync(zipFileName, CreationCollisionOption.ReplaceExisting);

            Stream zipToCreate = await zipFile.OpenStreamForWriteAsync();

            ZipArchive archive = new ZipArchive(zipToCreate, ZipArchiveMode.Update);
            await ZipFolderContents(sourceFolder, archive, sourceFolder.Path);
            archive.Dispose();
        }

        static private string GetCompressedFileName(string baseDirPath, StorageFile file)
        {
            return file.Path.Remove(0, baseDirPath.Length);
        }

        async public static Task UnZipFile(StorageFolder zipFileDirectory, string zipFilename, StorageFolder extractFolder = null)
        {
            if (extractFolder == null) extractFolder = zipFileDirectory;

            var folder = ApplicationData.Current.LocalFolder;

            using (var zipStream = await folder.OpenStreamForReadAsync(zipFilename))
            {
                using (MemoryStream zipMemoryStream = new MemoryStream((int)zipStream.Length))
                {
                    await zipStream.CopyToAsync(zipMemoryStream);

                    using (var archive = new ZipArchive(zipMemoryStream, ZipArchiveMode.Read))
                    {
                        foreach (ZipArchiveEntry entry in archive.Entries)
                        {
                            if (entry.Name != "")
                            {
                                using (Stream fileData = entry.Open())
                                {
                                    StorageFile outputFile = await extractFolder.CreateFileAsync(entry.FullName, CreationCollisionOption.ReplaceExisting);
                                    using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
                                    {
                                        await fileData.CopyToAsync(outputFileStream);
                                        await outputFileStream.FlushAsync();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }      
    }
}

Class can be as used follows:
 Windows.Storage.Pickers.FolderPicker fldPicker = new Windows.Storage.Pickers.FolderPicker();
  fldPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;                        fldPicker.FileTypeFilter.Add("*");

 StorageFolder sorceFolder = await fldPicker.PickSingleFolderAsync();

  await FolderZip.ZipFolder(sorceFolder, Windows.Storage.ApplicationData.Current.LocalFolder, "Sample.zip");
   await FolderZip.UnZipFile(Windows.Storage.ApplicationData.Current.LocalFolder, "Sample.zip");


For  UnZipFile we can pass third argument as null or skip for extraction in same folder.

If any mistakes i did suggest me to correct.

Thanks so much for reading my post.
 

4 comments:

  1. Hey Shashi, greetings from Brazil!

    I really enjoyed your effort in developing a class to extract Zip files in WinRT.

    Based in your code, I made the following implementations, so you can can extract subfolders.


    I created these two methods:

    private async Task CreateRecursiveFolder(StorageFolder folder, ZipArchiveEntry entry)
    {
    var steps = entry.FullName.Split('/').ToList();

    steps.RemoveAt(steps.Count() - 1);

    foreach (var i in steps)
    {
    await folder.CreateFolderAsync(i, CreationCollisionOption.OpenIfExists);

    folder = await folder.GetFolderAsync(i);
    }
    }

    private async Task ExtractFile(StorageFolder folder, ZipArchiveEntry entry)
    {
    var steps = entry.FullName.Split('/').ToList();

    steps.RemoveAt(steps.Count() - 1);

    foreach (var i in steps)
    {
    folder = await folder.GetFolderAsync(i);
    }

    using (Stream fileData = entry.Open())
    {
    StorageFile outputFile = await folder.CreateFileAsync(entry.Name, CreationCollisionOption.ReplaceExisting);

    using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
    {
    await fileData.CopyToAsync(outputFileStream);
    await outputFileStream.FlushAsync();
    }
    }
    }

    And the foreach which runs each file ended like this:

    foreach (ZipArchiveEntry entry in archive.Entries)
    {

    if (entry.Name == "")
    {
    // Folder
    await CreateRecursiveFolder(folder, entry);
    }
    else
    {
    // File
    await ExtractFile(folder, entry);
    }
    }

    Cheers!

    ReplyDelete
  2. Hi!
    Thanks for your code. We are from Uruguay.
    I have a problem when unzip the Zip created in a Mac.
    You have any solution?
    Some places, talk about turn of the zip64 property.

    Thanks!!

    ReplyDelete
    Replies
    1. Wii you send me the sample zip file created in mac

      Delete
  3. Error extracting large files bacause of these string:

    using (MemoryStream zipMemoryStream = new MemoryStream((int)zipStream.Length))
    await zipStream.CopyToAsync(zipMemoryStream);

    You don't need MemoryStream.

    ReplyDelete