Universal Windows Platform – ZipFile App

ZipFile App demonstrates how to create New and Open Zip archives plus Add, Remove and Extract files.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Library.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Library.cs the following should be entered:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;

public class ZipItem
{
    public string Name { get; set; }
    public string ActualBytes { get; set; }
    public string CompressedBytes { get; set; }
}

public class Library
{
    public const string extension_all = "*";
    public const string extension_zip = ".zip";
    public const string desc_archive = "Archive";
    public const string desc_file = "File";

    private string _token = string.Empty;
    StorageItemAccessList _access = StorageApplicationPermissions.FutureAccessList;

    public string Name { get; set; }

    private async Task<byte[]> GetByteFromFile(StorageFile storageFile)
    {
        using (IRandomAccessStream stream = await storageFile.OpenReadAsync())
        {
            using (DataReader dataReader = new DataReader(stream))
            {
                byte[] bytes = new byte[stream.Size];
                await dataReader.LoadAsync((uint)stream.Size);
                dataReader.ReadBytes(bytes);
                return bytes;
            }
        }
    }

    private async Task<StorageFile> OpenFileAsync(string extension)
    {
        try
        {
            FileOpenPicker picker = new FileOpenPicker()
            {
                SuggestedStartLocation = PickerLocationId.ComputerFolder
            };
            picker.FileTypeFilter.Add(extension);
            StorageFile open = await picker.PickSingleFileAsync();
            if (open != null)
            {
                return open;
            }
        }
        finally
        {
        }
        return null;
    }

    private async Task<StorageFile> SaveFileAsync(string filename, string extension, string description)
    {
        try
        {
            FileSavePicker picker = new FileSavePicker()
            {
                SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
                DefaultFileExtension = extension,
                SuggestedFileName = filename
            };
            picker.FileTypeChoices.Add(description, new List<string>() { extension });
            StorageFile save = await picker.PickSaveFileAsync();
            if (save != null)
            {
                return save;
            }
        }
        finally
        {
        }
        return null;
    }

    public async Task<List<ZipItem>> List()
    {
        List<ZipItem> results = null;
        StorageFile file = await _access.GetFileAsync(_token);
        if (file != null)
        {
            using (Stream stream = await file.OpenStreamForReadAsync())
            {
                using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read))
                {
                    results = new List<ZipItem>();
                    foreach (ZipArchiveEntry entry in archive.Entries)
                    {
                        if (entry.Name != string.Empty)
                        {
                            results.Add(new ZipItem()
                            {
                                Name = entry.Name,
                                CompressedBytes = $"Compressed Size {entry.CompressedLength}",
                                ActualBytes = $"Actual Size {entry.Length}"
                            });
                        }
                    }
                }
            }
        }
        return results;
    }

    public async Task<bool> NewAsync()
    {
        StorageFile file = await SaveFileAsync(desc_archive, extension_zip, desc_archive);
        if (file != null)
        {
            _token = _access.Add(file);
            Name = file.Name;
            using (Stream stream = await file.OpenStreamForWriteAsync())
            {
                new ZipArchive(stream, ZipArchiveMode.Create);
            }
            return true;
        }
        return false;
    }

    public async Task<List<ZipItem>> OpenAsync()
    {
        StorageFile file = await OpenFileAsync(extension_zip);
        if (file != null)
        {
            Name = file.Name;
            _token = _access.Add(file);
        }
        return await List();
    }

    public async Task<List<ZipItem>> AddAsync()
    {
        if (!string.IsNullOrEmpty(_token))
        {
            StorageFile file = await _access.GetFileAsync(_token);
            if (file != null)
            {
                StorageFile source = await OpenFileAsync(extension_all);
                if (source != null)
                {
                    using (Stream stream = await file.OpenStreamForWriteAsync())
                    {
                        using (ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Update))
                        {
                            ZipArchiveEntry existing = zipArchive.GetEntry(source.Name);
                            if (existing != null)
                            {
                                existing.Delete();
                            }
                            ZipArchiveEntry entry = zipArchive.CreateEntry(source.Name);
                            using (Stream entryStream = entry.Open())
                            {
                                byte[] data = await GetByteFromFile(source);
                                entryStream.Write(data, 0, data.Length);
                            }
                        }
                    }
                }
            }
            return await List();
        }
        return null;
    }

    public async void Extract(ZipItem item)
    {
        if (!string.IsNullOrEmpty(_token))
        {
            StorageFile file = await _access.GetFileAsync(_token);
            if (file != null)
            {
                using (Stream stream = await file.OpenStreamForReadAsync())
                {
                    using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read))
                    {
                        ZipArchiveEntry entry = archive.GetEntry(item.Name);
                        if (entry != null)
                        {
                            StorageFile source = await SaveFileAsync(entry.Name, Path.GetExtension(entry.Name), desc_file);
                            if (source != null)
                            {
                                using (Stream output = await source.OpenStreamForWriteAsync())
                                {
                                    await entry.Open().CopyToAsync(output);
                                    await output.FlushAsync();
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public async Task<List<ZipItem>> RemoveAsync(ZipItem item)
    {
        if (!string.IsNullOrEmpty(_token))
        {
            StorageFile file = await _access.GetFileAsync(_token);
            if (file != null)
            {
                using (Stream stream = await file.OpenStreamForWriteAsync())
                {
                    using (ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Update))
                    {
                        ZipArchiveEntry entry = zipArchive.GetEntry(item.Name);
                        if (entry != null)
                        {
                            entry.Delete();
                        }
                    }
                }
                return await List();
            }
        }
        return null;
    }
}

There’s a ZipItem Class to represent each item within the Archive then in the Library Class there are const values to set file extensions and descriptions plus variables for a “token” and StorageItemAccessList. There’s a GetByteFromFile method that converts a StorageFile into a byte[] with the use of a IRandomAccessStream plus a DataReader. The OpenFileAsync method uses a FileOpenPicker to select a file with the passed in extension and returns a StorageFile and SaveFileAsync uses a FileSavePicker to return a StorageFile that will be created and written to with the passed in filename, extension and description, and there’s a string property to store the name of the archive.

Also in the Library Class there’s a List method that uses a StorageFile from the StorageItemAccessList using the “token” – if this file is present then a Stream is used to then get the ZipArchive from and then each ZipArchiveEntry in the archive is looped through to create a list of ZipItem and is returned my that method. The NewAsync method uses the SaveFileAsync to create the StorageFile to create a new archive and returns true if successful or false if not. The OpenAsync method uses the OpenFileAsync to open an archive from the StorageFile and returns the list of ZipItem.

Again also in the Library Class is an AddAsync method uses a StorageFile from the StorageItemAccessList using the “token”. It then uses the OpenFileAsync to get the file to add to the archive – if the archive file is present then a Stream is used to then get the ZipArchive and then it checks for an existing entry – if there is one then it is deleted with Delete if not then a new entry is created with CreateEntry using the GetByteFromFile to insert the file into the archive. The Extract method is used to retrieve a file from an archive and uses a StorageFile from the StorageItemAccessList using the “token” and if this file is present then a Stream is used to then get the ZipArchive and then an entry is retrieved by name with the GetEntry method and then if present the SaveFileAsync method is used to get a StorageFile to write the file Stream to, then there’s a RemoveAsync method to remove a given item from the archive.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Grid Margin="50">
	<Grid.RowDefinitions>
		<RowDefinition Height="Auto"/>
		<RowDefinition Height="*"/>
	</Grid.RowDefinitions>
	<TextBlock Grid.Row="0" Name="Label" Margin="5" Style="{StaticResource SubtitleTextBlockStyle}"/>
	<ListBox Grid.Row="1" Name="Display">
		<ListBox.ItemContainerStyle>
			<Style TargetType="ListBoxItem">
				<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
			</Style>
		</ListBox.ItemContainerStyle>
		<ListBox.ItemTemplate>
			<DataTemplate>
				<Grid>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="40*"/>
						<ColumnDefinition Width="20*"/>
						<ColumnDefinition Width="20*"/>
						<ColumnDefinition Width="Auto"/>
					</Grid.ColumnDefinitions>
					<Grid Padding="5" Grid.Column="0" Background="{ThemeResource AccentButtonBackground}">
						<TextBlock Text="{Binding Name}" VerticalAlignment="Center"
						Foreground="{ThemeResource AccentButtonForeground}"/>
					</Grid>
					<Grid Padding="5" Grid.Column="1" Background="{ThemeResource AccentButtonForeground}">
						<TextBlock Text="{Binding ActualBytes}" VerticalAlignment="Center"
						Foreground="{ThemeResource AccentButtonBackground}"/>
					</Grid>
					<Grid Padding="5" Grid.Column="2" Background="{ThemeResource AccentButtonForeground}">
						<TextBlock Text="{Binding CompressedBytes}" VerticalAlignment="Center"
						Foreground="{ThemeResource AccentButtonBackground}"/>
					</Grid>
					<StackPanel Grid.Column="3" Orientation="Horizontal">
						<AppBarButton Name="Extract" Icon="SaveLocal" Label="Extract" Tag="{Binding}" Click="Extract_Click"/>
						<AppBarButton Name="Remove" Icon="Remove" Label="Remove" Tag="{Binding}" Click="Remove_Click"/>
					</StackPanel>
				</Grid>
			</DataTemplate>
		</ListBox.ItemTemplate>
	</ListBox>
</Grid>
<CommandBar VerticalAlignment="Bottom">
	<AppBarButton Icon="Page2" Label="Open" Click="New_Click"/>
	<AppBarButton Icon="OpenFile" Label="Open" Click="Open_Click"/>
	<AppBarButton Icon="Add" Label="Add" Click="Add_Click"/>
</CommandBar>

The MainPage has an AppBarButton for New, Open and Add. Then theres a ListBox that will have a list of ZipItem set to it’s DataSource and has a DataTemplate that displays a list of Name, Actual Size and Compressed Size for each entry in an archive plus an AppBarButton for Extract and Remove for each archive entry item displayed.

Step 11

From the Menu choose View and then Code

vs2017-view-code

Step 12

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

Library library = new Library();

private async void New_Click(object sender, RoutedEventArgs e)
{
	if (await library.NewAsync())
	{
		Label.Text = library.Name;
	}
}

private async void Open_Click(object sender, RoutedEventArgs e)
{
	Display.ItemsSource = await library.OpenAsync();
	Label.Text = library.Name;
}

private async void Add_Click(object sender, RoutedEventArgs e)
{
	Display.ItemsSource = await library.AddAsync();
}

private void Extract_Click(object sender, RoutedEventArgs e)
{
	ZipItem item = (ZipItem)((AppBarButton)sender).Tag;
	library.Extract(item);
}

private async void Remove_Click(object sender, RoutedEventArgs e)
{
	ZipItem item = (ZipItem)((AppBarButton)sender).Tag;
	Display.ItemsSource = await library.RemoveAsync(item);
}

Below the MainPage() Method an instance of the Library Class is created, then New_Click is used to create a new Zip File, Open_Click will call OpenAsync to open a Zip File and then you can use Add_Click to add to the Archive, Extract_Click to Extract a file and Remove_Click to remove a file from the Archive.

Step 13

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 14

After the Application has started running you can then select New to create a Zip archive using a FileSavePicker then you need to use Add to add to the archive using the FileOpenPicker. You can view any files in the archive in the ListBox plus you can Extract a file using a FileSavePicker or Remove a file from the archive.

ran-zipfile-app

Step 15

To Exit the Application select the Close button in the top right of the Application

vs2017-close

This example shows how you can use the ZipArchive compression features in System.IO.Compression and use this within a Universal Windows Platform application – usually this is used directly with paths but due to the sandboxed nature of UWP apps the lesser seen methods demonstrated in the example are used instead. This example demonstrates all the key features of dealing with a zip archive and is pretty close to the functionality within Windows 10 itself for dealing with Zip files.

Creative Commons License

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s