Use a compactação Zopfli em seu CDN

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.

Cenário
Cenário
Cenário

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 ZopfliPNGOptionsobjeto.

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 ZopfliOptionsobjeto 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/