004 [Xamarin] 在Android 中 , 解壓縮Zip檔案

Post date: 2015/12/8 下午 12:00:43

原作者 http://no2don.blogspot.com/search/label/Xamarin

前一陣子,公司專案要解壓縮Zip , 因為是用 Xamarin 找了一下,不是要錢不然就是我看不太懂,最後我改了一個Open Source (https://zipstorer.codeplex.com/)的專案終於成功解壓縮,這邊特別感謝網路上無私的大大門,這邊我也貢獻出我修改過後測試成功的東西..

1.首先畫面為

Image 021

其中我在Assets中放入一個sample.zip的壓縮檔,壓縮檔裡面是一個 sample.txt的檔案裏面有一些文字,我目標是把這檔案先copy到Download\ 下 ,之後解壓縮並起將zip中的sample.txt 文字內容show 出

Image 022

2. 之後在專案中加入此 ZipStorer.cs , C# code 如下,其中有地方是原本專案複製過來會compile 不過的部分,我這邊是已經修正的版本,在xamarin 下面compile會過而且可以使用

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Android.App;

using Android.Content;

using Android.OS;

using Android.Runtime;

using Android.Views;

using Android.Widget;

namespace System.IO.Compression

{

/// <summary>

/// Unique class for compression/decompression file. Represents a Zip file.

/// </summary>

public class ZipStorer : IDisposable

{

/// <summary>

/// Compression method enumeration

/// </summary>

public enum Compression : ushort

{

/// <summary>Uncompressed storage</summary>

Store = 0,

/// <summary>Deflate compression method</summary>

Deflate = 8

}

/// <summary>

/// Represents an entry in Zip file directory

/// </summary>

public struct ZipFileEntry

{

/// <summary>Compression method</summary>

public Compression Method;

/// <summary>Full path and filename as stored in Zip</summary>

public string FilenameInZip;

/// <summary>Original file size</summary>

public uint FileSize;

/// <summary>Compressed file size</summary>

public uint CompressedSize;

/// <summary>Offset of header information inside Zip storage</summary>

public uint HeaderOffset;

/// <summary>Offset of file inside Zip storage</summary>

public uint FileOffset;

/// <summary>Size of header information</summary>

public uint HeaderSize;

/// <summary>32-bit checksum of entire file</summary>

public uint Crc32;

/// <summary>Last modification time of file</summary>

public DateTime ModifyTime;

/// <summary>User comment for file</summary>

public string Comment;

/// <summary>True if UTF8 encoding for filename and comments, false if default (CP 437)</summary>

public bool EncodeUTF8;

/// <summary>Overriden method</summary>

/// <returns>Filename in Zip</returns>

public override string ToString()

{

return this.FilenameInZip;

}

}

#region Public fields

/// <summary>True if UTF8 encoding for filename and comments, false if default (CP 437)</summary>

public bool EncodeUTF8 = false;

/// <summary>Force deflate algotithm even if it inflates the stored file. Off by default.</summary>

public bool ForceDeflating = false;

#endregion

#region Private fields

// List of files to store

private List<ZipFileEntry> Files = new List<ZipFileEntry>();

// Filename of storage file

private string FileName;

// Stream object of storage file

private Stream ZipFileStream;

// General comment

private string Comment = "";

// Central dir image

private byte[] CentralDirImage = null;

// Existing files in zip

private ushort ExistingFiles = 0;

// File access for Open method

private FileAccess Access;

// Static CRC32 Table

private static UInt32[] CrcTable = null;

// Default filename encoder

private static Encoding DefaultEncoding = Encoding.GetEncoding(437);

#endregion

#region Public methods

// Static constructor. Just invoked once in order to create the CRC32 lookup table.

static ZipStorer()

{

// Generate CRC32 table

CrcTable = new UInt32[256];

for (int i = 0; i < CrcTable.Length; i++)

{

UInt32 c = (UInt32)i;

for (int j = 0; j < 8; j++)

{

if ((c & 1) != 0)

c = 3988292384 ^ (c >> 1);

else

c >>= 1;

}

CrcTable[i] = c;

}

}

/// <summary>

/// Method to create a new storage file

/// </summary>

/// <param name="_filename">Full path of Zip file to create</param>

/// <param name="_comment">General comment for Zip file</param>

/// <returns>A valid ZipStorer object</returns>

public static ZipStorer Create(string _filename, string _comment)

{

Stream stream = new FileStream(_filename, FileMode.Create, FileAccess.ReadWrite);

ZipStorer zip = Create(stream, _comment);

zip.Comment = _comment;

zip.FileName = _filename;

return zip;

}

/// <summary>

/// Method to create a new zip storage in a stream

/// </summary>

/// <param name="_stream"></param>

/// <param name="_comment"></param>

/// <returns>A valid ZipStorer object</returns>

public static ZipStorer Create(Stream _stream, string _comment)

{

ZipStorer zip = new ZipStorer();

zip.Comment = _comment;

zip.ZipFileStream = _stream;

zip.Access = FileAccess.Write;

return zip;

}

/// <summary>

/// Method to open an existing storage file

/// </summary>

/// <param name="_filename">Full path of Zip file to open</param>

/// <param name="_access">File access mode as used in FileStream constructor</param>

/// <returns>A valid ZipStorer object</returns>

public static ZipStorer Open(string _filename, FileAccess _access)

{

Stream stream = (Stream)new FileStream(_filename, FileMode.Open, _access == FileAccess.Read ? FileAccess.Read : FileAccess.ReadWrite);

ZipStorer zip = Open(stream, _access);

zip.FileName = _filename;

return zip;

}

/// <summary>

/// Method to open an existing storage from stream

/// </summary>

/// <param name="_stream">Already opened stream with zip contents</param>

/// <param name="_access">File access mode for stream operations</param>

/// <returns>A valid ZipStorer object</returns>

public static ZipStorer Open(Stream _stream, FileAccess _access)

{

if (!_stream.CanSeek && _access != FileAccess.Read)

throw new InvalidOperationException("Stream cannot seek");

ZipStorer zip = new ZipStorer();

//zip.FileName = _filename;

zip.ZipFileStream = _stream;

zip.Access = _access;

if (zip.ReadFileInfo())

return zip;

throw new System.IO.InvalidDataException();

}

/// <summary>

/// Add full contents of a file into the Zip storage

/// </summary>

/// <param name="_method">Compression method</param>

/// <param name="_pathname">Full path of file to add to Zip storage</param>

/// <param name="_filenameInZip">Filename and path as desired in Zip directory</param>

/// <param name="_comment">Comment for stored file</param>

public void AddFile(Compression _method, string _pathname, string _filenameInZip, string _comment)

{

if (Access == FileAccess.Read)

throw new InvalidOperationException("Writing is not alowed");

FileStream stream = new FileStream(_pathname, FileMode.Open, FileAccess.Read);

AddStream(_method, _filenameInZip, stream, File.GetLastWriteTime(_pathname), _comment);

stream.Close();

}

/// <summary>

/// Add full contents of a stream into the Zip storage

/// </summary>

/// <param name="_method">Compression method</param>

/// <param name="_filenameInZip">Filename and path as desired in Zip directory</param>

/// <param name="_source">Stream object containing the data to store in Zip</param>

/// <param name="_modTime">Modification time of the data to store</param>

/// <param name="_comment">Comment for stored file</param>

public void AddStream(Compression _method, string _filenameInZip, Stream _source, DateTime _modTime, string _comment)

{

if (Access == FileAccess.Read)

throw new InvalidOperationException("Writing is not alowed");

long offset;

if (this.Files.Count == 0)

offset = 0;

else

{

ZipFileEntry last = this.Files[this.Files.Count - 1];

offset = last.HeaderOffset + last.HeaderSize;

}

// Prepare the fileinfo

ZipFileEntry zfe = new ZipFileEntry();

zfe.Method = _method;

zfe.EncodeUTF8 = this.EncodeUTF8;

zfe.FilenameInZip = NormalizedFilename(_filenameInZip);

zfe.Comment = (_comment == null ? "" : _comment);

// Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc.

zfe.Crc32 = 0; // to be updated later

zfe.HeaderOffset = (uint)this.ZipFileStream.Position; // offset within file of the start of this local record

zfe.ModifyTime = _modTime;

// Write local header

WriteLocalHeader(ref zfe);

zfe.FileOffset = (uint)this.ZipFileStream.Position;

// Write file to zip (store)

Store(ref zfe, _source);

_source.Close();

this.UpdateCrcAndSizes(ref zfe);

Files.Add(zfe);

}

/// <summary>

/// Updates central directory (if pertinent) and close the Zip storage

/// </summary>

/// <remarks>This is a required step, unless automatic dispose is used</remarks>

public void Close()

{

if (this.Access != FileAccess.Read)

{

uint centralOffset = (uint)this.ZipFileStream.Position;

uint centralSize = 0;

if (this.CentralDirImage != null)

this.ZipFileStream.Write(CentralDirImage, 0, CentralDirImage.Length);

for (int i = 0; i < Files.Count; i++)

{

long pos = this.ZipFileStream.Position;

this.WriteCentralDirRecord(Files[i]);

centralSize += (uint)(this.ZipFileStream.Position - pos);

}

if (this.CentralDirImage != null)

this.WriteEndRecord(centralSize + (uint)CentralDirImage.Length, centralOffset);

else

this.WriteEndRecord(centralSize, centralOffset);

}

if (this.ZipFileStream != null)

{

this.ZipFileStream.Flush();

this.ZipFileStream.Dispose();

this.ZipFileStream = null;

}

}

/// <summary>

/// Read all the file records in the central directory

/// </summary>

/// <returns>List of all entries in directory</returns>

public List<ZipFileEntry> ReadCentralDir()

{

if (this.CentralDirImage == null)

throw new InvalidOperationException("Central directory currently does not exist");

List<ZipFileEntry> result = new List<ZipFileEntry>();

for (int pointer = 0; pointer < this.CentralDirImage.Length;)

{

uint signature = BitConverter.ToUInt32(CentralDirImage, pointer);

if (signature != 0x02014b50)

break;

bool encodeUTF8 = (BitConverter.ToUInt16(CentralDirImage, pointer + 8) & 0x0800) != 0;

ushort method = BitConverter.ToUInt16(CentralDirImage, pointer + 10);

uint modifyTime = BitConverter.ToUInt32(CentralDirImage, pointer + 12);

uint crc32 = BitConverter.ToUInt32(CentralDirImage, pointer + 16);

uint comprSize = BitConverter.ToUInt32(CentralDirImage, pointer + 20);

uint fileSize = BitConverter.ToUInt32(CentralDirImage, pointer + 24);

ushort filenameSize = BitConverter.ToUInt16(CentralDirImage, pointer + 28);

ushort extraSize = BitConverter.ToUInt16(CentralDirImage, pointer + 30);

ushort commentSize = BitConverter.ToUInt16(CentralDirImage, pointer + 32);

uint headerOffset = BitConverter.ToUInt32(CentralDirImage, pointer + 42);

uint headerSize = (uint)(46 + filenameSize + extraSize + commentSize);

Encoding encoder = encodeUTF8 ? Encoding.UTF8 : DefaultEncoding;

ZipFileEntry zfe = new ZipFileEntry();

zfe.Method = (Compression)method;

zfe.FilenameInZip = encoder.GetString(CentralDirImage, pointer + 46, filenameSize);

zfe.FileOffset = GetFileOffset(headerOffset);

zfe.FileSize = fileSize;

zfe.CompressedSize = comprSize;

zfe.HeaderOffset = headerOffset;

zfe.HeaderSize = headerSize;

zfe.Crc32 = crc32;

zfe.ModifyTime = DosTimeToDateTime(modifyTime);

if (commentSize > 0)

zfe.Comment = encoder.GetString(CentralDirImage, pointer + 46 + filenameSize + extraSize, commentSize);

result.Add(zfe);

pointer += (46 + filenameSize + extraSize + commentSize);

}

return result;

}

/// <summary>

/// Copy the contents of a stored file into a physical file

/// </summary>

/// <param name="_zfe">Entry information of file to extract</param>

/// <param name="_filename">Name of file to store uncompressed data</param>

/// <returns>True if success, false if not.</returns>

/// <remarks>Unique compression methods are Store and Deflate</remarks>

public bool ExtractFile(ZipFileEntry _zfe, string _filename)

{

// Make sure the parent directory exist

string path = System.IO.Path.GetDirectoryName(_filename);

if (!Directory.Exists(path))

Directory.CreateDirectory(path);

// Check it is directory. If so, do nothing

if (Directory.Exists(_filename))

return true;

Stream output = new FileStream(_filename, FileMode.Create, FileAccess.Write);

bool result = ExtractFile(_zfe, output);

if (result)

output.Close();

//if (_zfe.ModifyTime != null)

//{

// File.SetCreationTime(_filename, _zfe.ModifyTime);

//}

//if (_zfe.ModifyTime != null)

//{

// File.SetLastWriteTime(_filename, _zfe.ModifyTime);

//}

return result;

}

/// <summary>

/// Copy the contents of a stored file into an opened stream

/// </summary>

/// <param name="_zfe">Entry information of file to extract</param>

/// <param name="_stream">Stream to store the uncompressed data</param>

/// <returns>True if success, false if not.</returns>

/// <remarks>Unique compression methods are Store and Deflate</remarks>

public bool ExtractFile(ZipFileEntry _zfe, Stream _stream)

{

if (!_stream.CanWrite)

throw new InvalidOperationException("Stream cannot be written");

// check signature

byte[] signature = new byte[4];

this.ZipFileStream.Seek(_zfe.HeaderOffset, SeekOrigin.Begin);

this.ZipFileStream.Read(signature, 0, 4);

if (BitConverter.ToUInt32(signature, 0) != 0x04034b50)

return false;

// Select input stream for inflating or just reading

Stream inStream;

if (_zfe.Method == Compression.Store)

inStream = this.ZipFileStream;

else if (_zfe.Method == Compression.Deflate)

inStream = new DeflateStream(this.ZipFileStream, CompressionMode.Decompress, true);

else

return false;

// Buffered copy

byte[] buffer = new byte[16384];

this.ZipFileStream.Seek(_zfe.FileOffset, SeekOrigin.Begin);

uint bytesPending = _zfe.FileSize;

while (bytesPending > 0)

{

int bytesRead = inStream.Read(buffer, 0, (int)Math.Min(bytesPending, buffer.Length));

_stream.Write(buffer, 0, bytesRead);

bytesPending -= (uint)bytesRead;

}

_stream.Flush();

if (_zfe.Method == Compression.Deflate)

inStream.Dispose();

return true;

}

/// <summary>

/// Removes one of many files in storage. It creates a new Zip file.

/// </summary>

/// <param name="_zip">Reference to the current Zip object</param>

/// <param name="_zfes">List of Entries to remove from storage</param>

/// <returns>True if success, false if not</returns>

/// <remarks>This method only works for storage of type FileStream</remarks>

public static bool RemoveEntries(ref ZipStorer _zip, List<ZipFileEntry> _zfes)

{

if (!(_zip.ZipFileStream is FileStream))

throw new InvalidOperationException("RemoveEntries is allowed just over streams of type FileStream");

//Get full list of entries

List<ZipFileEntry> fullList = _zip.ReadCentralDir();

//In order to delete we need to create a copy of the zip file excluding the selected items

string tempZipName = Path.GetTempFileName();

string tempEntryName = Path.GetTempFileName();

try

{

ZipStorer tempZip = ZipStorer.Create(tempZipName, string.Empty);

foreach (ZipFileEntry zfe in fullList)

{

if (!_zfes.Contains(zfe))

{

if (_zip.ExtractFile(zfe, tempEntryName))

{

tempZip.AddFile(zfe.Method, tempEntryName, zfe.FilenameInZip, zfe.Comment);

}

}

}

_zip.Close();

tempZip.Close();

File.Delete(_zip.FileName);

File.Move(tempZipName, _zip.FileName);

_zip = ZipStorer.Open(_zip.FileName, _zip.Access);

}

catch

{

return false;

}

finally

{

if (File.Exists(tempZipName))

File.Delete(tempZipName);

if (File.Exists(tempEntryName))

File.Delete(tempEntryName);

}

return true;

}

#endregion

#region Private methods

// Calculate the file offset by reading the corresponding local header

private uint GetFileOffset(uint _headerOffset)

{

byte[] buffer = new byte[2];

this.ZipFileStream.Seek(_headerOffset + 26, SeekOrigin.Begin);

this.ZipFileStream.Read(buffer, 0, 2);

ushort filenameSize = BitConverter.ToUInt16(buffer, 0);

this.ZipFileStream.Read(buffer, 0, 2);

ushort extraSize = BitConverter.ToUInt16(buffer, 0);

return (uint)(30 + filenameSize + extraSize + _headerOffset);

}

/* Local file header:

local file header signature 4 bytes (0x04034b50)

version needed to extract 2 bytes

general purpose bit flag 2 bytes

compression method 2 bytes

last mod file time 2 bytes

last mod file date 2 bytes

crc-32 4 bytes

compressed size 4 bytes

uncompressed size 4 bytes

filename length 2 bytes

extra field length 2 bytes

filename (variable size)

extra field (variable size)

*/

private void WriteLocalHeader(ref ZipFileEntry _zfe)

{

long pos = this.ZipFileStream.Position;

Encoding encoder = _zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding;

byte[] encodedFilename = encoder.GetBytes(_zfe.FilenameInZip);

this.ZipFileStream.Write(new byte[] { 80, 75, 3, 4, 20, 0 }, 0, 6); // No extra header

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(_zfe.EncodeUTF8 ? 0x0800 : 0)), 0, 2); // filename and comment encoding

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)_zfe.Method), 0, 2); // zipping method

this.ZipFileStream.Write(BitConverter.GetBytes(DateTimeToDosTime(_zfe.ModifyTime)), 0, 4); // zipping date and time

this.ZipFileStream.Write(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 12); // unused CRC, un/compressed size, updated later

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedFilename.Length), 0, 2); // filename length

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // extra length

this.ZipFileStream.Write(encodedFilename, 0, encodedFilename.Length);

_zfe.HeaderSize = (uint)(this.ZipFileStream.Position - pos);

}

/* Central directory's File header:

central file header signature 4 bytes (0x02014b50)

version made by 2 bytes

version needed to extract 2 bytes

general purpose bit flag 2 bytes

compression method 2 bytes

last mod file time 2 bytes

last mod file date 2 bytes

crc-32 4 bytes

compressed size 4 bytes

uncompressed size 4 bytes

filename length 2 bytes

extra field length 2 bytes

file comment length 2 bytes

disk number start 2 bytes

internal file attributes 2 bytes

external file attributes 4 bytes

relative offset of local header 4 bytes

filename (variable size)

extra field (variable size)

file comment (variable size)

*/

private void WriteCentralDirRecord(ZipFileEntry _zfe)

{

Encoding encoder = _zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding;

byte[] encodedFilename = encoder.GetBytes(_zfe.FilenameInZip);

byte[] encodedComment = encoder.GetBytes(_zfe.Comment);

this.ZipFileStream.Write(new byte[] { 80, 75, 1, 2, 23, 0xB, 20, 0 }, 0, 8);

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(_zfe.EncodeUTF8 ? 0x0800 : 0)), 0, 2); // filename and comment encoding

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)_zfe.Method), 0, 2); // zipping method

this.ZipFileStream.Write(BitConverter.GetBytes(DateTimeToDosTime(_zfe.ModifyTime)), 0, 4); // zipping date and time

this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.Crc32), 0, 4); // file CRC

this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.CompressedSize), 0, 4); // compressed file size

this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.FileSize), 0, 4); // uncompressed file size

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedFilename.Length), 0, 2); // Filename in zip

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // extra length

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedComment.Length), 0, 2);

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // disk=0

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // file type: binary

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // Internal file attributes

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0x8100), 0, 2); // External file attributes (normal/readable)

this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.HeaderOffset), 0, 4); // Offset of header

this.ZipFileStream.Write(encodedFilename, 0, encodedFilename.Length);

this.ZipFileStream.Write(encodedComment, 0, encodedComment.Length);

}

/* End of central dir record:

end of central dir signature 4 bytes (0x06054b50)

number of this disk 2 bytes

number of the disk with the

start of the central directory 2 bytes

total number of entries in

the central dir on this disk 2 bytes

total number of entries in

the central dir 2 bytes

size of the central directory 4 bytes

offset of start of central

directory with respect to

the starting disk number 4 bytes

zipfile comment length 2 bytes

zipfile comment (variable size)

*/

private void WriteEndRecord(uint _size, uint _offset)

{

Encoding encoder = this.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding;

byte[] encodedComment = encoder.GetBytes(this.Comment);

this.ZipFileStream.Write(new byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }, 0, 8);

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)Files.Count + ExistingFiles), 0, 2);

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)Files.Count + ExistingFiles), 0, 2);

this.ZipFileStream.Write(BitConverter.GetBytes(_size), 0, 4);

this.ZipFileStream.Write(BitConverter.GetBytes(_offset), 0, 4);

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedComment.Length), 0, 2);

this.ZipFileStream.Write(encodedComment, 0, encodedComment.Length);

}

// Copies all source file into storage file

private void Store(ref ZipFileEntry _zfe, Stream _source)

{

byte[] buffer = new byte[16384];

int bytesRead;

uint totalRead = 0;

Stream outStream;

long posStart = this.ZipFileStream.Position;

long sourceStart = _source.Position;

if (_zfe.Method == Compression.Store)

outStream = this.ZipFileStream;

else

outStream = new DeflateStream(this.ZipFileStream, CompressionMode.Compress, true);

_zfe.Crc32 = 0 ^ 0xffffffff;

do

{

bytesRead = _source.Read(buffer, 0, buffer.Length);

totalRead += (uint)bytesRead;

if (bytesRead > 0)

{

outStream.Write(buffer, 0, bytesRead);

for (uint i = 0; i < bytesRead; i++)

{

_zfe.Crc32 = ZipStorer.CrcTable[(_zfe.Crc32 ^ buffer[i]) & 0xFF] ^ (_zfe.Crc32 >> 8);

}

}

} while (bytesRead == buffer.Length);

outStream.Flush();

if (_zfe.Method == Compression.Deflate)

outStream.Dispose();

_zfe.Crc32 ^= 0xffffffff;

_zfe.FileSize = totalRead;

_zfe.CompressedSize = (uint)(this.ZipFileStream.Position - posStart);

// Verify for real compression

if (_zfe.Method == Compression.Deflate && !this.ForceDeflating && _source.CanSeek && _zfe.CompressedSize > _zfe.FileSize)

{

// Start operation again with Store algorithm

_zfe.Method = Compression.Store;

this.ZipFileStream.Position = posStart;

this.ZipFileStream.SetLength(posStart);

_source.Position = sourceStart;

this.Store(ref _zfe, _source);

}

}

/* DOS Date and time:

MS-DOS date. The date is a packed value with the following format. Bits Description

0-4 Day of the month (1?1)

5-8 Month (1 = January, 2 = February, and so on)

9-15 Year offset from 1980 (add 1980 to get actual year)

MS-DOS time. The time is a packed value with the following format. Bits Description

0-4 Second divided by 2

5-10 Minute (0?9)

11-15 Hour (0?3 on a 24-hour clock)

*/

private uint DateTimeToDosTime(DateTime _dt)

{

return (uint)(

(_dt.Second / 2) | (_dt.Minute << 5) | (_dt.Hour << 11) |

(_dt.Day << 16) | (_dt.Month << 21) | ((_dt.Year - 1980) << 25));

}

private DateTime DosTimeToDateTime(uint _dt)

{

return new DateTime(

(int)(_dt >> 25) + 1980,

(int)(_dt >> 21) & 15,

(int)(_dt >> 16) & 31,

(int)(_dt >> 11) & 31,

(int)(_dt >> 5) & 63,

(int)(_dt & 31) * 2);

}

/* CRC32 algorithm

The 'magic number' for the CRC is 0xdebb20e3.

The proper CRC pre and post conditioning

is used, meaning that the CRC register is

pre-conditioned with all ones (a starting value

of 0xffffffff) and the value is post-conditioned by

taking the one's complement of the CRC residual.

If bit 3 of the general purpose flag is set, this

field is set to zero in the local header and the correct

value is put in the data descriptor and in the central

directory.

*/

private void UpdateCrcAndSizes(ref ZipFileEntry _zfe)

{

long lastPos = this.ZipFileStream.Position; // remember position

this.ZipFileStream.Position = _zfe.HeaderOffset + 8;

this.ZipFileStream.Write(BitConverter.GetBytes((ushort)_zfe.Method), 0, 2); // zipping method

this.ZipFileStream.Position = _zfe.HeaderOffset + 14;

this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.Crc32), 0, 4); // Update CRC

this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.CompressedSize), 0, 4); // Compressed size

this.ZipFileStream.Write(BitConverter.GetBytes(_zfe.FileSize), 0, 4); // Uncompressed size

this.ZipFileStream.Position = lastPos; // restore position

}

// Replaces backslashes with slashes to store in zip header

private string NormalizedFilename(string _filename)

{

string filename = _filename.Replace('\\', '/');

int pos = filename.IndexOf(':');

if (pos >= 0)

filename = filename.Remove(0, pos + 1);

return filename.Trim('/');

}

// Reads the end-of-central-directory record

private bool ReadFileInfo()

{

if (this.ZipFileStream.Length < 22)

return false;

try

{

this.ZipFileStream.Seek(-17, SeekOrigin.End);

BinaryReader br = new BinaryReader(this.ZipFileStream);

do

{

this.ZipFileStream.Seek(-5, SeekOrigin.Current);

UInt32 sig = br.ReadUInt32();

if (sig == 0x06054b50)

{

this.ZipFileStream.Seek(6, SeekOrigin.Current);

UInt16 entries = br.ReadUInt16();

Int32 centralSize = br.ReadInt32();

UInt32 centralDirOffset = br.ReadUInt32();

UInt16 commentSize = br.ReadUInt16();

// check if comment field is the very last data in file

if (this.ZipFileStream.Position + commentSize != this.ZipFileStream.Length)

return false;

// Copy entire central directory to a memory buffer

this.ExistingFiles = entries;

this.CentralDirImage = new byte[centralSize];

this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin);

this.ZipFileStream.Read(this.CentralDirImage, 0, centralSize);

// Leave the pointer at the begining of central dir, to append new files

this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin);

return true;

}

} while (this.ZipFileStream.Position > 0);

}

catch { }

return false;

}

#endregion

#region IDisposable Members

/// <summary>

/// Closes the Zip file stream

/// </summary>

public void Dispose()

{

this.Close();

}

#endregion

}

}

3. 為了觀察步驟,我將sample.zip 會先複製到 下載的檔案夾,Downloads\ 之後再解壓縮,所以在此記得打開裡個權限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

4.解壓縮的部分

結果:

程式範例:

標籤: Android , C# , Xamarin

Screenshot_2015-08-10-14-16-31

button.Click += delegate

{

//複製到Download/sample.zip的路徑

var copyFilePath = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads) + "/sample.zip");

//即將解壓縮的路徑

var targetPath = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads) +"/");

//將資源檔複製到下載區 方便解壓縮

var zipStream = Assets.Open("sample.zip");

var data = ReadFully(zipStream);

System.IO.File.WriteAllBytes(copyFilePath.AbsolutePath, data);

//讀取Download/sample.zip 並且解壓縮

ZipStorer zip = ZipStorer.Open(copyFilePath.AbsolutePath, FileAccess.Read);

List<ZipStorer.ZipFileEntry> dir = zip.ReadCentralDir();

string path2;

bool result;

foreach (ZipStorer.ZipFileEntry entry in dir)

{

path2 = Path.Combine(targetPath.AbsolutePath, Path.GetFileName(entry.FilenameInZip));

result = zip.ExtractFile(entry, path2);

}

zip.Close();

RunOnUiThread(() =>

{

//成功後打開 Download/sample.txt 並且將內容顯示出來

Toast.MakeText(this, System.IO.File.ReadAllText(targetPath.AbsolutePath + Java.IO.File.Separator +"sample.txt"), ToastLength.Short).Show();

});

};

Screenshot_2015-08-10-14-34-17