/*
* $Id: GzipCompressor.cs 439 2007-11-26 13:26:10Z spocke $
*
* @author Moxiecode
* @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved.
*/
using System;
using System.Web;
using System.Collections;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
namespace umbraco.editorControls.tinyMCE3.webcontrol.plugin
{
///
/// Description of GzipCompressor.
///
public class GzipCompressor {
#region private
private bool diskCache, noCompression;
private string cachePath;
private ArrayList items;
#endregion
///
///
///
public GzipCompressor() {
this.items = new ArrayList();
}
///
///
///
public bool NoCompression {
get { return noCompression; }
set { noCompression = value; }
}
///
///
///
public bool DiskCache {
get { return diskCache; }
set { diskCache = value; }
}
///
///
///
public string CachePath {
get { return cachePath; }
set { cachePath = value; }
}
///
///
///
///
public void AddFile(string path) {
//System.Web.HttpContext.Current.Response.Write(path + "\n");
if (File.Exists(path))
this.items.Add(new CompressItem(CompressItemType.File, path));
else
throw new Exception("File could not be found \"" + path + "\", unable to add it for Gzip compression.");
}
///
///
///
///
public void AddData(string data) {
this.items.Add(new CompressItem(CompressItemType.Chunk, data));
}
///
///
///
///
public void Compress(Stream to_stream) {
GZipStream gzipStream = null;
string key = "";
byte[] bytes;
// Build cache key
foreach (CompressItem item in this.items) {
if (item.Type == CompressItemType.File)
key += item.Data;
}
key = MD5(key);
if (this.NoCompression) {
this.SendPlainText(key, to_stream);
return;
}
// Check for cached file on disk, stream that one if it was found
if (this.DiskCache && File.Exists(Path.Combine(this.CachePath, key + ".gz"))) {
this.StreamFromTo(File.OpenRead(Path.Combine(this.CachePath, key + ".gz")), to_stream, 4096, CloseMode.InStream);
return;
}
try {
// Build new gzipped stream
if (this.DiskCache) {
gzipStream = new GZipStream(File.OpenWrite(Path.Combine(this.CachePath, key + ".gz")), CompressionMode.Compress);
// Compress all files into memory
foreach (CompressItem item in this.items) {
// Add file
if (item.Type == CompressItemType.File)
StreamFromTo(File.OpenRead(item.Data), gzipStream, 4096, CloseMode.InStream);
// Add chunk
if (item.Type == CompressItemType.Chunk) {
bytes = Encoding.ASCII.GetBytes(item.Data.ToCharArray());
gzipStream.Write(bytes, 0, bytes.Length);
}
}
// Close gzip stream
gzipStream.Close();
gzipStream = null;
// Send cached file to user
this.StreamFromTo(File.OpenRead(Path.Combine(this.CachePath, key + ".gz")), to_stream, 4096, CloseMode.InStream);
} else {
gzipStream = new GZipStream(to_stream, CompressionMode.Compress);
// Compress all files into output stream
foreach (CompressItem item in this.items) {
// Add file
if (item.Type == CompressItemType.File)
StreamFromTo(File.OpenRead(item.Data), gzipStream, 4096, CloseMode.InStream);
// Add chunk
if (item.Type == CompressItemType.Chunk) {
bytes = Encoding.ASCII.GetBytes(item.Data.ToCharArray());
gzipStream.Write(bytes, 0, bytes.Length);
}
}
}
} finally {
if (gzipStream != null)
gzipStream.Close();
}
}
#region private
private void SendPlainText(string key, Stream to_stream) {
Stream fileStream = null;
byte[] bytes;
// Check for cached file on disk, stream that one if it was found
if (this.DiskCache && File.Exists(Path.Combine(this.CachePath, key + ".js"))) {
this.StreamFromTo(File.OpenRead(Path.Combine(this.CachePath, key + ".js")), to_stream, 4096, CloseMode.InStream);
return;
}
// Build new plain text stream
if (this.DiskCache) {
try {
fileStream = File.OpenWrite(Path.Combine(this.CachePath, key + ".js"));
// Compress all files into memory
foreach (CompressItem item in this.items) {
// Add file
if (item.Type == CompressItemType.File)
StreamFromTo(File.OpenRead(item.Data), fileStream, 4096, CloseMode.InStream);
// Add chunk
if (item.Type == CompressItemType.Chunk) {
bytes = Encoding.ASCII.GetBytes(item.Data.ToCharArray());
fileStream.Write(bytes, 0, bytes.Length);
}
}
// Close file stream
fileStream.Close();
fileStream = null;
} finally {
// Close file stream
if (fileStream != null)
fileStream.Close();
}
// Send cached file to user
this.StreamFromTo(File.OpenRead(Path.Combine(this.CachePath, key + ".js")), to_stream, 4096, CloseMode.InStream);
} else {
// Concat all files into output stream
foreach (CompressItem item in this.items) {
// Add file
if (item.Type == CompressItemType.File)
StreamFromTo(File.OpenRead(item.Data), to_stream, 4096, CloseMode.InStream);
// Add chunk
if (item.Type == CompressItemType.Chunk) {
bytes = Encoding.ASCII.GetBytes(item.Data.ToCharArray());
to_stream.Write(bytes, 0, bytes.Length);
}
}
}
}
private void StreamFromTo(Stream in_stream, Stream out_stream, int buff_size, CloseMode mode) {
byte[] buff = new byte[buff_size];
int len;
try {
while ((len = in_stream.Read(buff, 0, buff_size)) > 0) {
out_stream.Write(buff, 0, len);
out_stream.Flush();
}
} finally {
if (in_stream != null && (mode == CloseMode.Both || mode == CloseMode.InStream))
in_stream.Close();
if (out_stream != null && (mode == CloseMode.Both || mode == CloseMode.OutStream))
out_stream.Close();
}
}
private string MD5(string str) {
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(Encoding.ASCII.GetBytes(str));
str = BitConverter.ToString(result);
return str.Replace("-", "");
}
#endregion
}
enum CloseMode {
None, InStream, OutStream, Both
}
enum CompressItemType {
File, Chunk
}
class CompressItem {
private string data;
private CompressItemType type;
public CompressItem(CompressItemType type, string data) {
this.type = type;
this.data = data;
}
public string Data {
get { return data; }
set { data = value; }
}
public CompressItemType Type {
get { return type; }
}
}
}