Downloading of zip file through ASP.NET MVC using DotNetZip

前端 未结 5 895
小蘑菇
小蘑菇 2020-12-03 04:03

I have created a text file in a folder and zipped that folder and saved @same location for test purpose. I wanted to download that zip file directly on user machine after it

相关标签:
5条回答
  • 2020-12-03 04:33

    You may use the controller's File method to return a file, like:

    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");
        }
    }
    

    If the zip file is not required otherwise to be stored, it is unnecessary to write it into a file on the server:

    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");
        }  
    }
    
    0 讨论(0)
  • 2020-12-03 04:34

    For those just wanting to return an existing Zip file from the App_Data folder (just dump in your zip files there), in the Home controller create this action method:

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

    Get File is an extention method:

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

    Home controller Index view looks like this:

    @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>
    

    The main index file action method:

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

    Where GetFileInformation is an extension method:

        public static List<FileInfo> GetFileInformation()
        {
            string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
            var dirInfo = new DirectoryInfo(path);
            return dirInfo.EnumerateFiles().ToList();
        }
    
    0 讨论(0)
  • 2020-12-03 04:37

    Create a GET-only controller action that returns a FileResult, like this:

    [HttpGet]
    public FileResult Download()
    {   
        // Create file on disk
        using (ZipFile zip = new ZipFile())
        {
            zip.AddDirectory(Server.MapPath("~/Directories/hello"));
            //zip.Save(Response.OutputStream);
            zip.Save(Server.MapPath("~/Directories/hello/sample.zip"));
        }
    
        // Read bytes from disk
        byte[] fileBytes = System.IO.File.ReadAllBytes(
            Server.MapPath("~/Directories/hello/sample.zip"));
        string fileName = "sample.zip";
    
        // Return bytes as stream for download
        return File(fileBytes, "application/zip", fileName);
    }
    
    0 讨论(0)
  • 2020-12-03 04:42

    just a fix to Klaus solution: (as I can not add comment I have to add another answer!)

    The solution is great but for me it gave corrupted zip file and I realized that it is because of return is before finalizing zip object so it did not close zip and result in a corrupted zip.

    so to fix we need to just move return line after using zip block so it works. the final result is :

    /// <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");
    }
    
    0 讨论(0)
  • 2020-12-03 04:44

    First of all, consider a way without creating any files on the server's disk. Bad practise. I'd recommend creating a file and zipping it in memory instead. Hope, you'll find my example below useful.

    /// <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 to the code above:

    1. Passing a MemoryStream instance requires checks that it's open, valid and etc. I omitted them. I'd rather passed a byte array of the file content instead of a MemoryStream instance to make the code more robust, but it'd be too much for this example.
    2. It doesn't show how to create a required context (your file) in memory. I'd refer to MemoryStream class for instructions.
    0 讨论(0)
提交回复
热议问题