I would like to add the SharpZipLib.Portable library to my Xamarin.Forms PCL project. I am targeting Android and iOS. The documentation mentions that you must implement a VirtualFileSystem in order to use the library, but I do not know how to do that and I have been unable to find much information on this topic.
Has anyone used this library that can guide me in the steps required to use it?
I ended up here when trying to implement the SharpZipLib.Portable.
I start using it without the IVirtualFileSystem because i had already a library called (PCLStorage) that knows how to read and write in filesystem (tested it on iOS and Android).
NOTE: This implementations are all inside a PCL targeting iOS, Android. No specific code for Android or iOS are needed.
Here is a simple example how to extract a Zip file using PCLStorage and SharpZipLib.Portable:
public async void DonwLoadAndStoreZipFile()
{
var bytes = await DownloadImageAsync("https://github.com/fluidicon.png");
// IFolder interface comes from PCLStorage
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder folder = await rootFolder.CreateFolderAsync("zipFolder", CreationCollisionOption.OpenIfExists);
IFile file = await folder.CreateFileAsync("test.zip" , CreationCollisionOption.OpenIfExists);
using (Stream stream = await file.OpenAsync(FileAccess.ReadAndWrite))
{
await stream.WriteAsync(bytes, 0, bytes.Length);
using (var zf = new ZipFile(stream))
{
foreach (ZipEntry zipEntry in zf)
{
// Gete Entry Stream.
Stream zipEntryStream = zf.GetInputStream(zipEntry);
// Create the file in filesystem and copy entry stream to it.
IFile zipEntryFile = await rootFolder.CreateFileAsync(zipEntry.Name , CreationCollisionOption.FailIfExists);
using(Stream outPutFileStream = await zipEntryFile.OpenAsync(FileAccess.ReadAndWrite))
{
await zipEntryStream.CopyToAsync(outPutFileStream);
}
}
}
}
}
If you want to get some examples on how to use the SharpZipLib.Portable you can read here (original SharpZipLib):
Code reference
and
Zip samples.
ALTERNATIVE:
After doing what i explained above I ended up with a much simpler solution because i only needed to support ZIP files.
I used ZipArchive Class present in System.IO.Compression and PCLStorage, so with this solution i don't used SharpZipLib.Portable.
Here is the version:
public async void DonwLoadAndStoreZipFile()
{
var bytes = await DownloadImageAsync(https://github.com/fluidicon.png);
// IFolder interface comes from PCLStorage
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder folder = await rootFolder.CreateFolderAsync("zipFolder", CreationCollisionOption.OpenIfExists);
IFile file = await folder.CreateFileAsync("test.zip" , CreationCollisionOption.OpenIfExists);
using (Stream stream = await file.OpenAsync(FileAccess.ReadAndWrite))
{
await stream.WriteAsync(bytes, 0, bytes.Length);
using(ZipArchive archive = new ZipArchive(stream))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
IFile zipEntryFile = await rootFolder.CreateFileAsync(entry.Name, CreationCollisionOption.FailIfExists);
using (Stream outPutStream = await zipEntryFile.OpenAsync(FileAccess.ReadAndWrite))
{
await entry.Open().CopyToAsync(outPutStream);
}
}
}
}
}
This worked for me:
Install SharpZipLib.Portable for PCL project only
Code to use it:
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
public void ExtractZipFile(string archivePath, string password, string outFolder) {
using(Stream fsInput = File.OpenRead(archivePath))
using(zf = new ZipFile(fs)){
if (!String.IsNullOrEmpty(password)) {
zf.Password = password;
}
foreach (ZipEntry zipEntry in zf) {
if (!zipEntry.IsFile) {
continue;
}
String entryFileName = zipEntry.Name;
var fullZipToPath = Path.Combine(outFolder, entryFileName);
var directoryName = Path.GetDirectoryName(fullZipToPath);
if (directoryName.Length > 0) {
Directory.CreateDirectory(directoryName);
}
var buffer = new byte[4096];
using(var zipStream = zf.GetInputStream(zipEntry))
using (Stream fsOutput = File.Create(fullZipToPath)) {
StreamUtils.Copy(zipStream, fsOutput , buffer);
}
}
}
}
Related
I was wondering how I can download an MP4 video file from a URI and save it to the media library on Windows Phone 8.1.
It would be great if it worked in a Universal App - but it doesn't have to.
I found this code to save an image to the camera roll - do I go the same way about this with an *.mp4 to save it to the video library? And can I just hand off a download stream (not sure if that makes sense) to that function?
StorageFolder testFolder = await StorageFolder.GetFolderFromPathAsync(#"C:\test");
StorageFile sourceFile = await testFolder.GetFileAsync("TestImage.jpg");
StorageFile destinationFile = await KnownFolders.CameraRoll.CreateFileAsync("MyTestImage.jpg");
using (var sourceStream = await sourceFile.OpenReadAsync())
{
using (var sourceInputStream = sourceStream.GetInputStreamAt(0))
{
using (var destinationStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
{
using (var destinationOutputStream = destinationStream.GetOutputStreamAt(0))
{
await RandomAccessStream.CopyAndCloseAsync(sourceInputStream, destinationStream);
}
}
}
}
So I finally figured it out, this is what my code looks like:
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var file = await response.Content.ReadAsByteArrayAsync();
StorageFile destinationFile
= await KnownFolders.SavedPictures.CreateFileAsync("file.mp4",
CreationCollisionOption.ReplaceExisting);
Windows.Storage.Streams.IRandomAccessStream stream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite);
IOutputStream output = stream.GetOutputStreamAt(0);
DataWriter writer = new DataWriter(output);
writer.WriteBytes(file);
await writer.StoreAsync();
await output.FlushAsync();
}
I have got a code from this link:
http://www.codeproject.com/Tips/515704/Archive-Multiple-Files-In-Zip-Extract-Zip-Archive
that compresses and extracts a file in zip format.
However the compressing part in the code just creates an empty zipfile, so how can i add files programmatically in this zip archieve?
i have checked the doc for the ziparchieve class and it has a method for .net called CreateEntryFromFile(String, String) , however this method doesn't apply for .net windows store version.
this is the code we are concerned with:
private async void ZipClick(object sender, RoutedEventArgs e)
{
FileSavePicker picker = new FileSavePicker();
picker.FileTypeChoices.Add("Zip Files (*.zip)", new List<string> { ".zip" });
picker.SuggestedStartLocation = PickerLocationId.Desktop;
picker.SuggestedFileName = "1";
zipFile = await picker.PickSaveFileAsync();
using (var zipStream = await zipFile.OpenStreamForWriteAsync())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create))
{
foreach (var file in storeFile)
{
ZipArchiveEntry entry = zip.CreateEntry(file.Name);
using (Stream ZipFile = entry.Open())
{
byte[] data = await GetByteFromFile(file);
ZipFile.Write(data, 0, data.Length);
}
}
}
}
}
I misunderstood the sample code, it actually can archive the the files you pass it for the first time, but i'm not sure if the same method can be used to add to add other files in the same zip file later.
public async void zipit(StorageFile zipFile, StorageFile file)
{
using (var zipToOpen = await zipFile.OpenStreamForWriteAsync())
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry(file.Name);
using (Stream writer = readmeEntry.Open())
{
//writer.WriteLine("Information about this package.");
//writer.WriteLine("========================");
byte[] data = await GetByteFromFile(file);
writer.Write(data, 0, data.Length);
}
}
}
}
where "zipFile" is the file you have chose to archive in (destination ) and "file" is the original non zipped file.
I am using this code to write into my file:
private async void play_Click(object sender, RoutedEventArgs e)
{
String MyScore;
Double previousScore = 0;
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
var dataFolder1 = await local.CreateFolderAsync("MyFolder", CreationCollisionOption.OpenIfExists);
var file1 = await dataFolder1.CreateFileAsync("MyFile.txt", CreationCollisionOption.OpenIfExists);
var file = await dataFolder1.OpenStreamForReadAsync("MyFile.txt");
using (StreamReader streamReader = new StreamReader(file))
{
MyScore = streamReader.ReadToEnd();
}
if (MyScore != null && !MyScore.Equals(""))
{
previousScore = Convert.ToDouble(MyScore);
}
Double CurerentScore = 0;
Double Total = 0;
String scoreText = this.ScoreTB.Text;
CurerentScore = Convert.ToDouble(scoreText);
Total = previousScore - CurerentScore;
using (var s = await file1.OpenStreamForWriteAsync())
{
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(Convert.ToString(Total));
s.Write(fileBytes, 0, fileBytes.Length);
}
}
But before writing into it, I want that my file should get cleared. What should I do?
This is what i have tried so far but the problem is that it writes the file up to the filebytes.length and due to that if the new information to be writed in file is less in terms of length in comparison to the privous length then some garbage value or unnecessay thing comes after the end of the new file
You can use this snippet :
var folder = ApplicationData.Current.LocalFolder;
// You are going to replace the file
var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenStreamForWriteAsync())
{
var content = System.Text.Encoding.UTF8.GetBytes(Convert.ToString(Total));
await stream.WriteAsync(content, 0, content.Length);
}
To quote the documentation :
ReplaceExisting : Create the new file or folder with the desired name,
and replaces any file or folder that already exists with that name.
I have clear the file by writing a empty string to it and then i have written what i wanted in my file This solved my issue as nothing was there in the file so whatever i wanted to write to it came up successfully.
Simply use Stream.SetLength like this:
using (var s = await file1.OpenStreamForWriteAsync())
{
// Add this line
s.SetLength(0);
// Then write new bytes. use 's.SetLength(fileBytes.Length)' if needed.
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(Convert.ToString(Total));
s.Write(fileBytes, 0, fileBytes.Length);
}
I am building a metro style app for windows 8 and I have a zip file that I am downloading from a web service, and I want to extract it.
I have seen the sample for compression and decompression, but that takes a single file an compresses/decompresses it. I have a whole directory structure that I need to extract.
Here is what I have so far:
var appData = ApplicationData.Current;
var file = await appData.LocalFolder.GetItemAsync("thezip.zip") as StorageFile;
var decompressedFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("tempFileName", CreationCollisionOption.GenerateUniqueName);
using (var decompressor = new Decompressor(await file.OpenSequentialReadAsync()))
using (var decompressedOutput = await decompressedFile.OpenAsync(FileAccessMode.ReadWrite))
{
var bytesDecompressed = await RandomAccessStream.CopyAsync(decompressor, decompressedOutput);
}
But this is no good, the bytesDecompressed variable is always zero size, but the zip File is 1.2MB
Any help here would be greatly appreciated.
EDIT: Answer, thanks to Mahantesh
Here is the code for unzipping a file:
private async void UnZipFile()
{
var folder = ApplicationData.Current.LocalFolder;
using (var zipStream = await folder.OpenStreamForReadAsync("thezip.zip"))
{
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 folder.CreateFileAsync(entry.Name, CreationCollisionOption.ReplaceExisting);
using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
{
await fileData.CopyToAsync(outputFileStream);
await outputFileStream.FlushAsync();
}
}
}
}
}
}
}
}
In Metro style apps, you work with compressed files by using the methods in the ZipArchive, ZipArchiveEntry, DeflateStream, and GZipStream classes.
Refer : UnZip File in Metro
Refer : Folder zip/unzip in metro c#
Based on your code and suggestions, I came up with one which supports folders extraction, which was one of my needs:
private async void UnZipFile(string file)
{
var folder = ApplicationData.Current.LocalFolder;
using (var zipStream = await folder.OpenStreamForReadAsync(file))
{
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 == "")
{
// Folder
await CreateRecursiveFolder(folder, entry);
}
else
{
// File
await ExtractFile(folder, entry);
}
}
}
}
}
}
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();
}
}
}
What is the best way to read amnd write and IO.Stream (Zip file downloded from internet in my case) to ApplicationData.Current.LocalFolder
I tried
public static async Task WriteToFile(
this System.IO.Stream input,
string fileName,
StorageFolder folder = null)
{
folder = folder ?? ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync(
fileName,
CreationCollisionOption.ReplaceExisting);
using (var fs = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (var outStream = fs.GetOutputStreamAt(0))
{
using (var dataWriter = new DataWriter(outStream))
{
byte[] buffer = new byte[8 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
dataWriter.WriteBytes(buffer);
}
await dataWriter.StoreAsync();
dataWriter.DetachStream();
}
await outStream.FlushAsync();
}
}
}
for writing and
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(filename);
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var stream = fileStream.AsStreamForRead();
but the file gets corrupted somewhere along the way.I do no think there is a problem with reading so it should be somewhere in writing the file. Is there a better way to write IO.Stream to ApplicationData.Current.LocalFolder that works?
Try this:
static async void DownloadFileAsync(
this HttpClient httpClient,
string requestUri,
string fileName,
StorageFolder folder = null)
{
folder = folder ?? ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync(
fileName, CreationCollisionOption.ReplaceExisting);
using (var httpStream = await httpClient.GetStreamAsync(uri))
using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await httpStream.CopyToAsync(fileStream.AsStreamForWrite());
}
}
MSDN: HttpClient Class, HttpClient.GetStreamAsync Method, WindowsRuntimeStreamExtensions.AsStreamForWrite Method, Stream.CopyToAsync Method.