Search code examples
asp.netresponse.transmitfile

Transmitting Zipped file from server in .Net


I am writing a small web app and part of the functionality is to be able to download the logs from the server to the user's file system. I am able to zip the existing logs, but am failing to get the zipped folder to transmit. I have browsed many other similar questions on here, but have yet to get it to work based on any of them.

Here is the code that I am currently trying:

.Net Controller

[HttpPost]
public ActionResult DownloadLogs()
{
    string path = System.Configuration.ConfigurationManager.AppSettings["LogPath"];

    try
    {

        Log.Information("Closing current logger. AudtiLog: {auditLog}", true);
        Log.CloseAndFlush();
        string startPath = path;
        string zipPath = "C:\\my_folder\\result.zip";
        string extractPath = path + "extract";

        //deleting existing zips
        if (System.IO.File.Exists(zipPath))
            System.IO.File.Delete(zipPath);
        if (System.IO.Directory.Exists(extractPath)) 
            System.IO.Directory.Delete(extractPath, true);

        ZipFile.CreateFromDirectory(startPath, zipPath);
        ZipFile.ExtractToDirectory(zipPath, extractPath);

        FileInfo file = new FileInfo(zipPath);

        using (FileStream filestream = new FileStream(zipPath, FileMode.Open))
        {
            return File(filestream, "application/zip", "ServerLogZip.zip");
        }

    }
    catch (Exception ex)
    {
        Log.Error("Error occurred when downloading server logs. Error: {Error}; AuditLog: {auditLog}", ex.Message, true);
        return Json(new { result = "error" });
    }

}

Javascript

function DownloadLogs() {

            $.ajax({
                type: "POST",
                url: "/ManageServer/DownloadLogs",
                contentType: "application/zip",
                success: function (response) {
                    alert("Success")
                },
                error: function (response) {
                    alert("Error");
                }
            });
        }

Whenever I run it, it zips the logs into one folder, steps through the Response portion of the code successfully, but nothing happens. I've tried debugging and stepping through the code, but haven't found the answer yet. I've also tried the Response.WriteFile method as well. No luck.

Edit I've updated the code to return ActionResult and returned a File. It is currently returning a 500 error from the server.


Solution

  • You have a problem on your code as @mason noticed. You are trying to return two things, the file and the status.

    The operation status should be checked through HTTP return codes.

    You can use the the IActionResult interface as return type so you can handle things right. Give the method a File as return type and if everthing is fine it will return your file with all headers needed. In case of something goes wrong, you can return a BadRequest("Error Message");

    The File return type accepts as parameters a FileStream that will contain your raw data, the Mime Type of the file and the filename

    To achieve that, do the following steps

    • Change the method return type to FileResult
    • Create a variable that will receive your file content as a Stream or use an using statement
    • Create a byte array to allocate the file content (If you try to return the filestream you will get an error of file closed because the using statement is closed before the retun occurs)
    • Return data like this return File(byteArray, "application/zip", "ServerLogZip.zip");

    Sample

    try{
        // Do things to prepare your file
    
        using(FileStream filestream = new FileStream(zipPath,FileMode.Open))
        {
            byte[] zipBytes= new byte[filestream.Length];
            filestream.Read(PhotoBytes, 0, PhotoBytes.Length);
            return File(zipBytes, "application/zip", "ServerLogZip.zip"); 
        }
    }
    catch(Exception ex){
        return BadRequest("Something gone wrong: " + ex.Message);
    }
    

    I've been burning my mind to figure out how to download this file through async request, but in the end of the day, I realized that maybe you don't need such a complex solution. You can just call the route and the file will be downloaded.

    function DownloadLogs() {
        document.location = your_route;
    }
    

    For this work properly, you must also change the method decorator of your C# method from [HttpPost] to [HttpGet]