I'm trying to zip some files up from the file system, and then return that through an Ajax request on my front end.
My document model contains all the information on where the file is located, as well as the name, so document.FullPath
is the fully qualified name of the file (path\filename.extension
).
I'm currently getting an error
Incorrect content-type: application/json
I don't understand why. It doesn't hit the controller action.
My controller looks like this:
[HttpPost]
public IActionResult DownloadMyDocuments(int userId)
{
DocumentViewModel documentViewModel= new();
documentViewModel.MyDocuments = _context.Documents.Where(x => x.UserId.Equals(userId)).ToList();
string contentType = null;
if (documentViewModel.MyDocuments != null)
{
using (var stream = new MemoryStream())
{
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
foreach (MyDocument document in documentViewModel.MyDocuments)
{
if (System.IO.File.Exists(document.FullPath))
{
contentType = FileHelper.GetMimeContentType(document.FullPath);
byte[] file = System.IO.File.ReadAllBytes(document.FullPath);
var archiveEntry = archive.CreateEntry(document.FileName, CompressionLevel.Fastest);
using (var zipStream = archiveEntry.Open())
{
zipStream.Write(file, 0, file.Length);
}
}
}
}
stream.Position = 0;
return File(stream.ToArray(), "application/zip", "Documents.zip");
}
}
}
My request on the frontend looks like this:
var UserId = $('#downloadDocsUserId').val();
$.ajax({
url: "@Url.Action("DownloadMyDocuments", "Home")",
type: 'POST',
dataType: "JSON",
data: UserId,
processData: true,
dataType: "application/zip",
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log("success", data);
},
error: function (data) {
console.log("error", data);
}
});
DataType is used to tell jQuery the type of data it expects to receive from the server, rather than specifying the type of data you send to the server. For file downloads, there is actually no need to specify the dataType since binary data (files) are expected, not JSON or other data formats. You can create a Blob object, specify the object's MIME type as "application/zip", indicating that it is a ZIP file, and use JavaScript to dynamically create a download link. After the download is completed, the link element is removed from the document. to maintain page clarity. Here is a sample code you can use as a reference:
<h2>Download My Documents</h2>
<button id="downloadButton">Download </button>
@section scripts {
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script>
$(document).ready(function () {
$("#downloadButton").click(function () {
var userId = 123;
$.ajax({
url: "@Url.Action("DownloadMyDocuments", "Download")" + "?userId=" + userId,
type: 'POST',
xhrFields: {
responseType: 'blob'
},
success: function (data) {
var myCustomBlob = new Blob([data], { type: "application/zip" });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(myCustomBlob);
link.download = "Documents.zip";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
error: function (xhr, status, error) {
console.log("Error:", error);
}
});
});
});
</script>
}
Mycontroller:
[HttpPost]
public IActionResult DownloadMyDocuments(int userId)
{
var documents = _context.Documents.Where(x => x.UserId == userId).ToList();
using (var stream = new MemoryStream())
{
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
foreach (var document in documents)
{
if (System.IO.File.Exists(document.FullPath))
{
byte[] file = System.IO.File.ReadAllBytes(document.FullPath);
var archiveEntry = archive.CreateEntry(document.FileName, CompressionLevel.Fastest);
using (var zipStream = archiveEntry.Open())
{
zipStream.Write(file, 0, file.Length);
}
}
}
}
stream.Position = 0;
return File(stream.ToArray(), "application/zip", "Documents.zip");
}