下载使用MVC5选择的多个文件

时间:2017-03-08 12:45:14

标签: javascript c# asp.net-mvc-5

我在MVC5中开发一个看起来像这样的视图:

enter image description here

我需要选择一个或多个表记录,并能够在数据库中下载以前保存的文件。我一直在寻找解决方案,并做了几个测试,但我找不到解决方案。我试图从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。

我做错了什么?非常感谢你!无视宽恕。

3 个答案:

答案 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.Namefile.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");
}