web-dev-qa-db-fra.com

Téléchargement du fichier Zip via ASP.NET MVC à l'aide de DotNetZip

J'ai créé un fichier texte dans un dossier et zippé ce dossier et enregistré le même emplacement à des fins de test. Je voulais télécharger ce fichier Zip directement sur la machine de l'utilisateur après sa création. J'utilise la bibliothèque dotnetzip et ai effectué les tâches suivantes:

Response.Clear();
Response.ContentType = "application/Zip";
Response.AddHeader("content-disposition", "filename=" + "sample.Zip");
using (ZipFile Zip = new ZipFile())
{
    Zip.AddDirectory(Server.MapPath("~/Directories/hello"));
    Zip.Save(Server.MapPath("~/Directories/hello/sample.Zip"));
}

Quelqu'un peut-il s'il vous plaît suggérer comment le fichier Zip peut être téléchargé à la fin de l'utilisateur.?

5
user2801336

Vous pouvez utiliser la méthode File du contrôleur pour renvoyer un fichier, tel que:

public ActionResult Download()
{
    using (ZipFile Zip = new ZipFile())
    {
        Zip.AddDirectory(Server.MapPath("~/Directories/hello"));
        Zip.Save(Server.MapPath("~/Directories/hello/sample.Zip"));
        return File(Server.MapPath("~/Directories/hello/sample.Zip"), 
                                   "application/Zip", "sample.Zip");
    }
}

Si le fichier Zip n'a pas besoin d'être stocké, il est inutile de l'écrire dans un fichier sur le serveur:

public ActionResult Download()
{
    using (ZipFile Zip = new ZipFile())
    {
        Zip.AddDirectory(Server.MapPath("~/Directories/hello"));

        MemoryStream output = new MemoryStream();
        Zip.Save(output);
        return File(output.ToArray(), "application/Zip", "sample.Zip");
    }  
}
16
mcserep

Tout d'abord, envisagez une solution sans créer de fichiers sur le disque du serveur. Mauvaise pratique. Je vous recommande de créer un fichier et de le zipper en mémoire à la place. J'espère que vous trouverez mon exemple ci-dessous utile.

/// <summary>
///     Zip a file stream
/// </summary>
/// <param name="originalFileStream"> MemoryStream with original file </param>
/// <param name="fileName"> Name of the file in the Zip container </param>
/// <returns> Return byte array of zipped file </returns>
private byte[] GetZippedFiles(MemoryStream originalFileStream, string fileName)
{
    using (MemoryStream zipStream = new MemoryStream())
    {
        using (ZipArchive Zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
        {
            var zipEntry = Zip.CreateEntry(fileName);
            using (var writer = new StreamWriter(zipEntry.Open()))
            {
                originalFileStream.WriteTo(writer.BaseStream);
            }
            return zipStream.ToArray();
        }
    }
}

/// <summary>
///     Download zipped file
/// </summary>
[HttpGet]
public FileContentResult Download()
{
    var zippedFile = GetZippedFiles(/* your stream of original file */, "hello");
    return File(zippedFile, // We could use just Stream, but the compiler gets a warning: "ObjectDisposedException: Cannot access a closed Stream" then.
                "application/Zip",
                "sample.Zip");
}

Notes au code ci-dessus:

  1. Passer une instance MemoryStream nécessite de vérifier qu’elle est ouverte, valide, etc. Je les ai omis. Je préférerais passer un tableau d'octets du contenu du fichier à la place d'une instance MemoryStream pour rendre le code plus robuste, mais ce serait trop pour cet exemple.
  2. Il ne montre pas comment créer un contexte requis (votre fichier) en mémoire. Je me référerais à MemoryStream class pour des instructions.
4
Alex Klaus

juste une solution à la solution de Klaus: (comme je ne peux pas ajouter de commentaire, je dois ajouter une autre réponse!) 

La solution est excellente mais pour moi, cela a donné un fichier Zip corrompu et je me suis rendu compte que c’est parce que le retour est avant la finalisation de l’objet Zip, de sorte qu’il ne ferme pas Zip et ne provoque pas un Zip corrompu.

pour résoudre ce problème, nous devons simplement déplacer la ligne de retour après avoir utilisé le bloc Zip pour que cela fonctionne. Le résultat final est le suivant:

/// <summary>
///     Zip a file stream
/// </summary>
/// <param name="originalFileStream"> MemoryStream with original file </param>
/// <param name="fileName"> Name of the file in the Zip container </param>
/// <returns> Return byte array of zipped file </returns>
private byte[] GetZippedFiles(MemoryStream originalFileStream, string fileName)
{
    using (MemoryStream zipStream = new MemoryStream())
    {
        using (ZipArchive Zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
        {
            var zipEntry = Zip.CreateEntry(fileName);
            using (var writer = new StreamWriter(zipEntry.Open()))
            {
                originalFileStream.WriteTo(writer.BaseStream);
            }
        }
        return zipStream.ToArray();
    }
}

/// <summary>
///     Download zipped file
/// </summary>
[HttpGet]
public FileContentResult Download()
{
    var zippedFile = GetZippedFiles(/* your stream of original file */, "hello");
    return File(zippedFile, // We could use just Stream, but the compiler gets a warning: "ObjectDisposedException: Cannot access a closed Stream" then.
                "application/Zip",
                "sample.Zip");
}
2
HamedH

Pour ceux qui souhaitent simplement renvoyer un fichier Zip existant à partir du dossier App_Data (il suffit de vider vos fichiers Zip), dans le contrôleur Accueil, créez cette méthode d'action:

    public FileResult DownLoad(string filename)
    {
        var content = XFile.GetFile(filename);
        return File(content, System.Net.Mime.MediaTypeNames.Application.Zip, filename);

    }

Get File est une méthode d'extention:

   public static byte[] GetFile(string name)
    {
        string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
        string filenanme = path + "/" + name;
        byte[] bytes = File.ReadAllBytes(filenanme);
        return bytes;
    }

La vue Index du contrôleur domestique se présente comme suit:

@model  List<FileInfo>

<table class="table">
    <tr>
        <th>
            @Html.DisplayName("File Name")
        </th>
        <th>
            @Html.DisplayName("Last Write Time")
        </th>
        <th>
            @Html.DisplayName("Length (mb)")
        </th>
        <th></th>
    </tr>

    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.ActionLink("DownLoad","DownLoad",new {filename=item.Name})
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.LastWriteTime)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Length)
            </td>
        </tr>
    }
</table>

La méthode d'action du fichier d'index principal:

    public ActionResult Index()
    {
        var names = XFile.GetFileInformation();
        return View(names);
    }

Où GetFileInformation est une méthode d'extension:

    public static List<FileInfo> GetFileInformation()
    {
        string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
        var dirInfo = new DirectoryInfo(path);
        return dirInfo.EnumerateFiles().ToList();
    }
0
John Peters