Vários meses atrás, o Google lançou o Zopfli , que é um algoritmo de compressão lento (cerca de 100 vezes mais lento do que o normal), que atinge uma compressão de 4 a 8% melhor.
Zopfli também foi adaptado para ser usado na compressão PNG:
ZopfliPNG não fará nada mais do que ler um arquivo png, recompactar as partes DEFLATED (dados de imagem IDAT e se a ferramenta amadurecer outros pedaços compactados como iTXt, zTXt e iCCP) e gravar o arquivo modificado.
Quando devemos usar isso? O melhor lugar para usar o Zopfli é em seu conteúdo estático, portanto, optamos por usá-lo em nosso CDN do Azure.
Zopfli é um algoritmo de compressão compatível com o algoritmo DEFLATE usado no zlib, permitindo que seja usado perfeitamente com programas e dispositivos já implantados que suportam o padrão. O Zopfli produz arquivos 4 a 8% menores que o zlib, em detrimento de ser substancialmente mais lento para compactar um arquivo do que outras implementações do algoritmo DEFLATE.
Ajuste perfeito, já que os arquivos CDN são carregados apenas uma vez e a compressão só precisa acontecer no upload. Os clientes que baixam arquivos compactados irão descompactá-los usando o DEFLATE normal suportado em todos os lugares na mesma velocidade (ou mais rápido, já que serão menores em tamanho).
Nossa primeira etapa foi colocar o Zopfli em C #. Então, criamos um wrapper e o lançamos no Apache 2.0
https://github.com/echovoice/libzopfli-sharp
O pacote Nuget também pode ser encontrado aqui: https://www.nuget.org/packages/libzopfli-sharp
Uso de compactação PNG
Se você estiver trabalhando com objetos .Net Image, basta chamar o método de extensão Image.SaveAsPNG()
Image testImage = Image.FromFile("files/ev.png");
testImage.SaveAsPNG(path_to_save_compressed_PNG);
Você pode compactar arquivos * .PNG diretamente usando o método.ZopfliPNG.compress()
string path = "files/ev.png";
ZopfliPNG.compress(path);
Também implementamos uma classe derivada de Stream chamada ZopfliPNGStream
byte[] uncompressed = File.ReadAllBytes("files/ev.png");
int before = uncompressed.Length;
byte[] compressed;
int after = 0;
using (MemoryStream compressStream = new MemoryStream())
using (ZopfliPNGStream compressor = new ZopfliPNGStream(compressStream))
{
compressor.Write(uncompressed, 0, before);
compressor.Close();
compressed = compressStream.ToArray();
after = compressed.Length;
}
Além de usar as opções de compactação padrão, o Zopfli expõe algumas opções adicionais para ajustar a compactação.
Estendemos isso no ZopfliPNGOptions
objeto.
public class ZopfliPNGOptions
{
// Allow altering hidden colors of fully transparent pixels
public Boolean lossy_transparent;
// Convert 16-bit per channel images to 8-bit per channel
public Boolean lossy_8bit;
// Filter strategies to try
public ZopfliPNGFilterStrategy[] filter_strategies;
// Automatically choose filter strategy using less good compression
public Boolean auto_filter_strategy;
// PNG chunks to keep
// chunks to literally copy over from the original PNG to the resulting one
public String[] keepchunks;
// Use Zopfli deflate compression
public Boolean use_zopfli;
// Zopfli number of iterations
public Int32 num_iterations;
// Zopfli number of iterations on large images
public Int32 num_iterations_large;
// 0=none, 1=first, 2=last, 3=both
public Int32 block_split_strategy;
}
Uso de compactação Gzip, Deflate e Zlib
Para todos os 3 tipos de compressão, implementamos uma classe derivada de Stream ZopfliStream
byte[] uncompressed = File.ReadAllBytes("files/fp.log");
int before = uncompressed.Length;
byte[] compressed;
int after = 0;
using (MemoryStream compressStream = new MemoryStream())
using (ZopfliStream compressor = new ZopfliStream(compressStream, ZopfliFormat.ZOPFLI_FORMAT_DEFLATE))
{
compressor.Write(uncompressed, 0, before);
compressor.Close();
compressed = compressStream.ToArray();
after = compressed.Length;
}
O segundo parâmetro para nossa classe Stream derivada é o tipo de compactação a ser usado.
public enum ZopfliFormat
{
ZOPFLI_FORMAT_GZIP,
ZOPFLI_FORMAT_ZLIB,
ZOPFLI_FORMAT_DEFLATE
};
Além de usar as opções padrão, o Zopfli expõe algumas opções adicionais usadas para ajustar a compressão.
Estendemos isso no ZopfliOptions
objeto que também pode ser passado para o Stream.
public class ZopfliOptions
{
// Whether to print output
public Int32 verbose;
// Whether to print more detailed output
public Int32 verbose_more;
// Maximum amount of times to rerun forward and backward pass to optimize LZ77
// compression cost. Good values: 10, 15 for small files, 5 for files over
// several MB in size or it will be too slow.
public Int32 numiterations;
// If true, splits the data in multiple deflate blocks with optimal choice
// for the block boundaries. Block splitting gives better compression. Default:
// true (1).
public Int32 blocksplitting;
// If true, chooses the optimal block split points only after doing the iterative
// LZ77 compression. If false, chooses the block split points first, then does
// iterative LZ77 on each individual block. Depending on the file, either first
// or last gives the best compression. Default: false (0).
public Int32 blocksplittinglast;
// Maximum amount of blocks to split into (0 for unlimited, but this can give
// extreme results that hurt compression on some files). Default value: 15.
public Int32 blocksplittingmax;
}
Para onde ir a partir daqui?
Agora que o Zopfli está funcionando em C #, você desejará agrupar qualquer um de seus uploads de Blob do Azure com o fluxo Zopfli.
// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
CloudConfigurationManager.GetSetting("StorageConnectionString"));
// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve reference to a previously created container.
CloudBlobContainer container = blobClient.GetContainerReference("mycontainer");
// Retrieve reference to a blob named "myblob".
CloudBlockBlob blockBlob = container.GetBlockBlobReference("myblob");
// Create or overwrite the "myblob" blob with contents from a local file.
byte[] uncompressed = File.ReadAllBytes(@"path\myfile");
int before = uncompressed.Length;
byte[] compressed;
// test deflate stream compression code
using (MemoryStream compressStream = new MemoryStream())
using (ZopfliStream compressor = new ZopfliStream(compressStream,ZopfliFormat.ZOPFLI_FORMAT_DEFLATE))
{
compressor.Write(uncompressed, 0, before);
compressor.Close();
blockBlob.UploadFromStream(compressStream);
}
do MSDN
http://www.windowsazure.com/en-us/develop/net/how-to-guides/blob-storage/
O MSDN tem um ótimo artigo sobre como habilitar o CDN de um Blob.
http://www.windowsazure.com/en-us/develop/net/common-tasks/cdn/
Se você ainda precisa de ajuda para fazer upload para o Azure ou como determinar se a compactação é compatível, eu sugiro ver este tutorial (um pouco desatualizado):
http://joelfillmore.com/serving-gzip-compressed-content-from-the-azure-cdn/