我在MVC5中开发一个看起来像这样的视图:
我需要选择一个或多个表记录,并能够在数据库中下载以前保存的文件。我一直在寻找解决方案,并做了几个测试,但我找不到解决方案。我试图从javascript发送选定的代码到控制器并从同一个下载文件,但我不能这样做。下载可能是您可以,获取所有文件并生成zip,或自动将所有文件全部下载到浏览器的默认文件夹。 我非常感谢能帮助我的人...问候!
这是我的ajax电话:
$.ajax({
type: "POST",
url: "/GestionGarantias/Garantias/Download",
data: { pCodigo: mCodigo },//Here I would send the list of //codes, but now to test, I only call the controller regardless of the value, //because the values in the list set them in the handy controller to test.
xhrFields: {
responseType: 'blob'
},
success: function (data) { //Here should I bring a zip with all files supposedly?
var a = document.createElement('a');
var url = window.URL.createObjectURL(data);
a.href = url;
a.download = 'myfile.zip'; //------------------> I UNDERSTAND THAT THIS IS THE NAME THAT WE SHOULD INDICATE FOR Download ... is it so?
a.click();
window.URL.revokeObjectURL(url);
},
error: function (request, status, errorThrown) {
//alert("Se produjo un error al descargar los adjuntos.");
}
});
我的控制器:
[HttpPost]
public ActionResult Download(List<String> codes)
{
codes = new List<string>();
codes.Add("1079");
codes.Add("1078");
codes.Add("1077");
MemoryStream ms = null;
foreach (string codigoGar in codes)
{
string mimetypeOfFile = "";
Garantias oGarantia = ControladorGarantias.getGarantia(SessionHelper.GetEntorno(), codigoGar);
var stream = new MemoryStream(oGarantia.comprobante);
ms = new MemoryStream();
byte[] buffer = new byte[256];
if (stream.Length >= 256)
stream.Read(buffer, 0, 256);
else
stream.Read(buffer, 0, (int)stream.Length);
try
{
System.UInt32 mimetype;
FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0);
System.IntPtr mimeTypePtr = new IntPtr(mimetype);
mimetypeOfFile = Marshal.PtrToStringUni(mimeTypePtr);
Marshal.FreeCoTaskMem(mimeTypePtr);
}
catch (Exception e)
{
return null;
}
string fileName = "";
if (!string.IsNullOrEmpty(mimetypeOfFile))
{
switch (mimetypeOfFile.ToLower())
{
case "application/pdf":
fileName = "Comprobante_" + oGarantia.nombreService + "_" + oGarantia.nroFactura + ".pdf";
break;
case "image/x-png":
fileName = "Comprobante_" + oGarantia.nombreService + "_" + oGarantia.nroFactura + ".png";
break;
case "image/pjpeg":
fileName = "Comprobante_" + oGarantia.nombreService + "_" + oGarantia.nroFactura + ".jpg";
break;
}
}
using (var zip = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
var entry = zip.CreateEntry(fileName);
using (var fileStream = stream)
using (var entryStream = entry.Open())
{
fileStream.CopyTo(entryStream);
}
}
}
return File(ms.ToArray(), "application/zip");
}
我所做的是在其中创建一个列表,仅用于测试。 我的想法是为表中选择的每个记录,找到他们的信息,(文件)并将它们添加到zip。
我做错了什么?非常感谢你!无视宽恕。
答案 0 :(得分:3)
你的问题有两个部分。首先,存在获取所有文件的问题,其次是下载它们的问题。在我们解决这些问题之前,让我们退后一步,了解请求 - 响应周期的工作原理。
使用HTTP,客户端发出请求,服务器返回响应。重要的是,这里有一个精确的1-1相关性。请求只能有一个响应。这意味着如果您需要提供多个文件作为下载,那么 可以将它们压缩,因为您只能返回一个文件而不是多个文件。创建一个zip文件允许您只返回一个文件,同时仍然满足允许用户一次下载所有文件的要求。
然后是AJAX的问题。 JavaScript中的XMLHttpRequest
对象本质上是瘦客户端。它发出HTTP请求并接收响应,但就是这样。与通过地址栏导航时Web浏览器发出的请求不同,自动响应没有任何操作,就像开发人员处理响应并实际发生某些事情一样。
有了这个,第一部分是创建一个可以返回zip文件作为响应的动作。这实际上很简单:您只需要返回FileResult
:
[HttpPost]
public ActionResult DownloadCodes(List<int> codes)
{
// use codes to get the appropriate files, however you do that
using (var ms = new MemoryStream())
using (var zip = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var file in files)
{
// write zip archive entries
}
return File(ms.ToArray(), "application/zip");
}
}
对于zip存档条目的编写,它取决于文件数据的来源。如果您有文件系统引用,那么您只需:
zip.CreateEntryFromFile(file, Path.GetFileName(file));
如果您有字节数组,例如您从数据库中的varbinary列返回的内容:
var entry = zip.CreateEntry(file.Name);
using (var fileStream = new MemoryStream(file.Data))
using (var entryStream = entry.Open())
{
fileStream.CopyTo(entryStream);
}
其中file.Name
和file.Data
分别构成属性,指的是存储文件名的位置以及存储文件数据的位置。
现在,您可以简单地对此操作执行常规表单发布,并且由于响应是在Web浏览器(zip存档)中无法查看的文件类型,因此浏览器将自动提示下载。此外,由于在浏览器中无法查看,因此选项卡/窗口中的实际视图也不会更改,从而无需使用AJAX来完成停留在同一页面上。但是,如果需要,您可以使用AJAX,但是您只能在现代浏览器中处理AJAX中的文件响应(基本上只有IE 10或更低版本)。如果您不需要支持旧版本的IE,那么您需要的代码将是:
<强>的jQuery 强>
$.ajax({
url: '/url/to/download/code/action',
data: data // where `data` is what you're posting, i.e. the list of codes
xhrFields: {
responseType: 'blob'
},
success: function (data) {
// handle file download
}
});
普通JavaScript
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function () {
var data = xhr.response;
// handle file download
}
xhr.open('POST', '/url/to/download/codes/action');
xhr.send();
无论您采取哪种方式,处理文件下载的代码都是:
var a = document.createElement('a');
var url = window.URL.createObjectURL(data);
a.href = url;
a.download = 'myfile.pdf';
a.click();
window.URL.revokeObjectURL(url);
答案 1 :(得分:0)
使用以下类下载文件:
public class FileDownloadResult : ContentResult
{
private string fileName;
private Stream fileData;
private bool downloadFlag;
public FileDownloadResult(string fileName, Stream fileData, bool downloadFlag)
{
this.fileName = fileName;
this.fileData = fileData;
this.downloadFlag = downloadFlag;
}
public override void ExecuteResult(ControllerContext context)
{
if (string.IsNullOrEmpty(this.fileName))
throw new Exception("A file name is required.");
if (this.fileData == null)
throw new Exception("File data is required.");
var contentDisposition = "";
if (!downloadFlag)
contentDisposition = string.Format("inline; filename={0}", this.fileName);
else
contentDisposition = string.Format("attachment; filename={0}", this.fileName);
context.HttpContext.Response.AddHeader("Content-Disposition", contentDisposition);
if (downloadFlag)
ContentType = "application/force-download";
else
{
if (this.fileName.IndexOf(".pdf", fileName.IndexOf(".")) > 0)
ContentType = "application/pdf";
else if (this.fileName.IndexOf(".csv", fileName.IndexOf(".")) > 0)
ContentType = "application/csv";
else if (this.fileName.IndexOf(".xls", fileName.IndexOf(".")) > 0)
ContentType = "application/xls";
else if (this.fileName.IndexOf(".xlsx", fileName.IndexOf(".")) > 0)
ContentType = "application/xslx";
else
ContentType = "application/txt";
}
context.HttpContext.Response.AddHeader("Content-Type", ContentType);
byte[] buffer = new byte[1024];
int count = 0;
while ((count = fileData.Read(buffer, 0, buffer.Length)) > 0)
{
context.HttpContext.Response.OutputStream.Write(buffer, 0, count);
context.HttpContext.Response.Flush();
}
}
}
然后在您的控制器中,使用如下:
public FileDownloadResult Download(string fileName, Stream fileStream)
{
return new FileDownloadResult(fileName, fileStream, true);
}
希望有所帮助:)
答案 2 :(得分:0)
我来自@Chris Pratt,不得不进行一些更改以使其对我有用
using (var ms = new MemoryStream())
{
using (var zip = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var file in files)
{
// write zip archive entries
zip.CreateEntryFromFile(file, Path.GetFileName(file), CompressionLevel.Optimal);
}
}
return File(ms.ToArray(), "application/zip");
}