Как стать автором
Обновить

Когда нужно ZIPовать на лету

Время на прочтение4 мин
Количество просмотров715
Задача тривиальная. Делаете какие то отчеты, файлы и хотите дать возможность пользователю их скачать в своем ASP.NET приложении. Почему полезно использовать архивацию?: а) уменьшается скачиваемый объем б) можно отдавать файлы пакетами по несколько.
В .net есть специальный класс для работы с GZip, находится в System.IO.Compression, и называется GZipStream, но он не позволяет хранить несколько файлов в одном архиве, такая специфика. Есть, конечно, энтузиасты, которые при помощи его создают и полноценные zip архивы (правда открывать они, вроде, могут их только при помощи своих программ — по крайней мере, мне попадались только такие).
В .NET 3.0 и выше можно использовать класс ZipPackage из System.IO.Packaging, который находится в сборке WindowsBase.DLL (находится примерно в C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll), не знаю почему данная сборка не лежит в GAC, вот тут есть пример, как это все использовать: Creating Zip archives in .NET (without an external library like SharpZipLib).
Но все же если используете SharpZipLib, как сделать архивирование в памяти? Наверняка, можно сделать через OutputZipStream. Но мне понравился класс FileZip, у которого есть метод Add(), в который можно передать имя файла, или готовый ZipEntry, а так же есть возможность передать объект с типом, унаследованным от IStaticDataSource.

Итак, реализуем класс, который будет использоваться в качестве Stream (потока) для архивирования классом FileZip:
/// <summary>
/// Реализация ресурса для зипования
/// </summary>
class MemoryStreamStaticDataSource : ICSharpCode.SharpZipLib.Zip.IStaticDataSource, IDisposable
{
  private MemoryStream MemoryStream { get; set; }

  /// <summary>
  /// Создаем ресурс с MemoryStream
  /// </summary>
  /// <param name="bytes"></param>
  public MemoryStreamStaticDataSource(byte[] bytes)
  {
    MemoryStream = new MemoryStream(bytes) { Position = 0 };
  }

  #region IStaticDataSource Members

  public Stream GetSource()
  {
    return MemoryStream;
  }

  #endregion

  #region IDisposable Members

  public void Dispose()
  {
    if (MemoryStream != null)
      MemoryStream.Dispose();
  }

  #endregion
}


* This source code was highlighted with Source Code Highlighter.

Файлы, пускай, у нас уже будут в виде набора байт (byte[]), идея в том, что эти файлы мы не хотим хранить физически, только в оперативной памяти. Задача — мы генерируем эти файлы сами, для примера я написал такой код:
/// <summary>
/// Пример, получаем данные первого сгенерированного файла
/// </summary>
/// <returns></returns>
private static byte[] GetFirstFileData()
{
  return GetFileData(@"Первый файл.");
}

/// <summary>
/// Пример, получаем данные второго сгенерированного файла
/// </summary>
/// <returns></returns>
private static byte[] GetSecondFileData()
{
  return GetFileData(@"Второй файл.");
}

private static byte[] GetFileData(string textdata)
{
  return Encoding.UTF8.GetBytes(textdata);
}


* This source code was highlighted with Source Code Highlighter.

Итак, последнее, используя класс ZipFile, добавляем в архив наши бутафорские файлы и возвращаем в Response страницы (вместо контента страницы).
/// <summary>
/// Переопределяем загрузку страницы, и возвращаем zip файл
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)
{
  base.OnLoad(e);

  Response.Clear();
  //Указывает MIME тип
  Response.ContentType = "application/zip";
  //Указываем, что приаттачен файл с именем file.zip
  Response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", HttpUtility.UrlEncodeUnicode("file.zip")));

  //Получаем данные(файл архива) и записываем в Response
  byte[] bytes = GetZipData();
  Response.OutputStream.Write(bytes, 0, bytes.Length);
}

private static byte[] GetZipData()
{
  //Создаем архив в памяти
  using (MemoryStream ms = new MemoryStream())
  {
    using (ICSharpCode.SharpZipLib.Zip.ZipFile file = new ICSharpCode.SharpZipLib.Zip.ZipFile(ms))
    {
      file.BeginUpdate();

      //Добавляем в архив первый файл
      file.Add(new MemoryStreamStaticDataSource(GetFirstFileData()), "file1.txt");
      //Добавляем в архив второй файл
      file.Add(new MemoryStreamStaticDataSource(GetSecondFileData()), "file2.txt");

      file.CommitUpdate();
    }

    return ms.ToArray();
  }
}


* This source code was highlighted with Source Code Highlighter.

В результате у нас получиться страница aspx, которая будет возвращать zip архив с двумя файлами.
Скачать пример...
Теги:
Хабы:
+6
Комментарии8

Публикации