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.
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
FileResult
using
statementbyte
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 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]