C# 如何使用MVC 4上传大文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16287706/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:39:30  来源:igfitidea点击:

How to upload large files using MVC 4?

c#jqueryasp.net-mvcfile-uploadplupload

提问by punkouter

I had it working.. but I noticed once the files I was uploading get bigger (around 4000k) the controller would not be called..

我让它工作了..但是我注意到一旦我上传的文件变大(大约 4000k),控制器就不会被调用..

So I added in chunking which fixed that problem.. but now when I open the file its full of garbage characters...

所以我添加了分块来解决这个问题......但是现在当我打开文件时它充满了垃圾字符......

So what is the correct way to upload large files with plupload/MVC 4 ?

那么使用 plupload/MVC 4 上传大文件的正确方法是什么?

Here is my current code

这是我当前的代码

  $(document).ready(function () {

    var uploader = new plupload.Uploader({
        runtimes: 'html5',
        browse_button: 'pickfiles',
        container: 'container',
     //   max_file_size: '20000mb',
        url: '@Url.Action("Upload", "Home")',
        chunk_size: '4mb',
        //filters: [
        //    { title: "Excel files", extensions: "xls,xlsx" },
        //    { title: "Text files", extensions: "txt" }
        //],
        multiple_queues: true,
        multipart: true,
        multipart_params: { taskId: '' }
    });

and the controller

和控制器

  [HttpPost]
    public ActionResult Upload(int? chunk, string name, string taskId)
    {
        string filePath = "";
        var fileUpload = Request.Files[0];
        var uploadPath = Server.MapPath("~/App_Data/Uploads");
        chunk = chunk ?? 0;
        string uploadedFilePath = Path.Combine(uploadPath, name);
        var fileName = Path.GetFileName(uploadedFilePath);

 try
        {
            using (var fs = new FileStream(filePath, chunk == 0 ? FileMode.Create : FileMode.Append))
            {
                var buffer = new byte[fileUpload.InputStream.Length];
                fileUpload.InputStream.Read(buffer, 0, buffer.Length);
                fs.Write(buffer, 0, buffer.Length);
            }

            //Log to DB for future processing
            InstanceExpert.AddProcessStart(filePath, Int32.Parse(taskId));
        }

采纳答案by ShaneKm

In web.config you need these (2GB all around):

在 web.config 中,您需要这些(周围 2GB):

<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" maxRequestLength="2147483647" executionTimeout="1600" requestLengthDiskThreshold="2147483647" />
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="2147483647" />
      </requestFiltering>
    </security>
    ...
</system.web>

回答by Prince Deekay

Current Version

当前版本

According to the detailed error description of IIS 8.0, which is the version I used at the time I wrote this answer, you need to verify the ">configuration/system.webServer/security/requestFiltering/requestLimits@maxAllowedContentLength setting in the ApplicationHost.configor Web.configfile. That means you need to include:

根据IIS 8.0的详细错误描述,这是我写这个答案时使用的版本,需要验证ApplicationHost.configWeb中">configuration/system.webServer/security/requestFiltering/requestLimits@maxAllowedContentLength设置.config文件。这意味着您需要包括:

<requestLimits maxAllowedContentLength="20971520000"></requestLimits>

inside configuration/system.webServer/security/requestFiltering tag tree. Just in case you lack the imagination to visualize where it goes, the full code block goes like the following:

在配置/system.webServer/security/requestFiltering 标签树中。以防万一您缺乏想象它的去向,完整的代码块如下所示:

<configuration>
    <system.webServer>
        <security>
            <requestFiltering>
                <requestLimits maxAllowedContentLength="20971520000"></requestLimits>
            </requestFiltering>
        </security>
    </system.webServer>
</configuration>

Visual Studio 2010/.Net Framework 4 and Before

Visual Studio 2010/.Net Framework 4 及之前版本

It is also possible that legacy web applications created with VS2008/10 and/or .Net Framework 3.5/4 may still be looking for this configuration via configuration/system.web/httpRuntime@maxRequestLength, but as evidenced by the linked page, it is no longer available, although HttpRuntime Class, which doesn't apply to this scenario, still exists since .Net Framework 1.1. If this is the case, you need to include:

也有可能使用 VS2008/10 和/或 .Net Framework 3.5/4 创建的遗留 Web 应用程序可能仍在通过configuration/system.web/httpRuntime@maxRequestLength查找此配置,但正如链接页面所证明的那样,它是不再可用,尽管不适用于这种情况的HttpRuntime Class自 .Net Framework 1.1 起仍然存在。如果是这种情况,您需要包括:

<httpRuntime maxRequestLength="20971520000" />

inside configuration/system.web/httpRuntime tag tree. Once again, just in case you lack the comprehensibility to figure out where it gets inserted, the full code block looks something like the following:

在配置/system.web/httpRuntime 标签树中。再一次,以防万一您缺乏确定插入位置的可理解性,完整的代码块如下所示:

<configuration>
    <system.web>
        <httpRuntime maxRequestLength="20971520000" />
    </system.web>
</configuration>

The file size number is just an arbitrary number (20,000 MB – not 20 GB, which would rather be 21,474,836,480) to show as a demo. Unless you're coding the website for a tight security group who has a need to upload large files, you shouldn't allow this big of a file size being uploaded to your web server.

文件大小数字只是一个任意数字(20,000 MB - 不是 20 GB,而是 21,474,836,480)以显示为演示。除非您为需要上传大文件的严格安全组编写网站代码,否则您不应允许将这么大的文件上传到您的 Web 服务器。

回答by Aryan Firouzian

The solution is based on Jonathan's code here. If you want to upload a large file, something like 1Gbyte video file, you have to chuck the file and send it through several request (one request gives time out). first you set the max limit for client and server side in Web.configas discussed in other answers.

解决方案基于乔纳森的代码here。如果你想上传一个大文件,比如 1Gbyte 的视频文件,你必须把文件夹起来并通过几个请求发送它(一个请求超时)。首先,如其他答案中所述,在Web.config 中设置客户端和服务器端的最大限制。

<system.webServer>
 <security>
  <requestFiltering>
    <requestLimits maxAllowedContentLength="2147483647" />
  </requestFiltering>
 </security>
<system.webServer>

and

<system.web>
  <httpRuntime targetFramework="4.5" maxRequestLength="2147483647" />
</system.web>

then chunk the file, and send each chuck, wait for response and send the next chunk. here is the html (VideoDivwork as upload panel), javascript (jQuery) and controller code.

然后将文件分块,并发送每个卡盘,等待响应并发送下一个块。这是 html(VideoDiv用作上传面板)、javascript(jQuery)和控制器代码。

    <div id="VideoDiv">
        <label>Filename:</label>
        <input type="file" id="fileInput" /><br/><br/>
        <input type="button" id="btnUpload" value="Upload a presentation"/><br/><br/>
        <div id="progressbar_container" style="width: 100%; height: 30px; position: relative; background-color: grey; display: none">
            <div id="progressbar" style="width: 0%; height: 100%; position: absolute; background-color: green"></div>
            <span id="progressbar_label" style="position: absolute; left: 35%; top: 20%">Uploading...</span>
        </div>
    </div>

Javascript code to chuck, call controller and update progressbar:

用于卡盘、调用控制器和更新进度条的 Javascript 代码:

        var progressBarStart = function() {
            $("#progressbar_container").show();
        }

        var progressBarUpdate = function (percentage) {
            $('#progressbar_label').html(percentage + "%");
            $("#progressbar").width(percentage + "%");
        }

        var progressBarComplete = function() {
            $("#progressbar_container").fadeOut(500);
        }

        var file;

        $('#fileInput').change(function(e) {
            file = e.target.files[0];
        });

        var uploadCompleted = function() {
            var formData = new FormData();
            formData.append('fileName', file.name);
            formData.append('completed', true);

            var xhr2 = new XMLHttpRequest();
            xhr2.onload = function() {
                progressBarUpdate(100);
                progressBarComplete();
            }
            xhr2.open("POST", "/Upload/UploadComplete?fileName=" + file.name + "&complete=" + 1, true);
            xhr2.send(formData);
        }

        var multiUpload = function(count, counter, blob, completed, start, end, bytesPerChunk) {
            counter = counter + 1;
            if (counter <= count) {
                var chunk = blob.slice(start, end);
                var xhr = new XMLHttpRequest();
                xhr.onload = function() {
                    start = end;
                    end = start + bytesPerChunk;
                    if (count == counter) {
                        uploadCompleted();
                    } else {
                        var percentage = (counter / count) * 100;
                        progressBarUpdate(percentage);
                        multiUpload(count, counter, blob, completed, start, end, bytesPerChunk);
                    }
                }
                xhr.open("POST", "/Upload/MultiUpload?id=" + counter.toString() + "&fileName=" + file.name, true);
                xhr.send(chunk);
            }
        }

        $("#VideoDiv").on("click", "#btnUpload", function() {
            var blob = file;
            var bytesPerChunk = 3757000;
            var size = blob.size;

            var start = 0;
            var end = bytesPerChunk;
            var completed = 0;
            var count = size % bytesPerChunk == 0 ? size / bytesPerChunk : Math.floor(size / bytesPerChunk) + 1;
            var counter = 0;
            progressBarStart();
            multiUpload(count, counter, blob, completed, start, end, bytesPerChunk);
        });

and here is the upload controller to store the chucnk in ("App_Data/Videos/Temp") and later merge them and store in ("App_Data/Videos"):

这是将 chucnk 存储在 ("App_Data/Videos/Temp") 中的上传控制器,然后将它们合并并存储在 ("App_Data/Videos") 中:

public class UploadController : Controller
{
    private string videoAddress = "~/App_Data/Videos";

    [HttpPost]
    public string MultiUpload(string id, string fileName)
    {
        var chunkNumber = id;
        var chunks = Request.InputStream;
        string path = Server.MapPath(videoAddress+"/Temp");
        string newpath = Path.Combine(path, fileName+chunkNumber);
        using (FileStream fs = System.IO.File.Create(newpath))
        {
            byte[] bytes = new byte[3757000];
            int bytesRead;
            while ((bytesRead=Request.InputStream.Read(bytes,0,bytes.Length))>0)
            {
                fs.Write(bytes,0,bytesRead);
            }
        }
        return "done";
    }

    [HttpPost]
    public string UploadComplete(string fileName, string complete)
    {
        string tempPath = Server.MapPath(videoAddress + "/Temp");
        string videoPath = Server.MapPath(videoAddress);
        string newPath = Path.Combine(tempPath, fileName);
        if (complete=="1")
        {
            string[] filePaths = Directory.GetFiles(tempPath).Where(p=>p.Contains(fileName)).OrderBy(p => Int32.Parse(p.Replace(fileName, "$").Split('$')[1])).ToArray();
            foreach (string filePath in filePaths)
            {
                MergeFiles(newPath, filePath);
            }
        }
        System.IO.File.Move(Path.Combine(tempPath, fileName),Path.Combine(videoPath,fileName));
        return "success";
    }

    private static void MergeFiles(string file1, string file2)
    {
        FileStream fs1 = null;
        FileStream fs2 = null;
        try
        {
            fs1 = System.IO.File.Open(file1, FileMode.Append);
            fs2 = System.IO.File.Open(file2, FileMode.Open);
            byte[] fs2Content = new byte[fs2.Length];
            fs2.Read(fs2Content, 0, (int) fs2.Length);
            fs1.Write(fs2Content, 0, (int) fs2.Length);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message + " : " + ex.StackTrace);
        }
        finally
        {
            if (fs1 != null) fs1.Close();
            if (fs2 != null) fs2.Close();
            System.IO.File.Delete(file2);
        }
    }
}

However, if two users at same time upload files with same name, there will be some problem, and you have to handle this issue. By reading responseText, you can catch some error and exception and trim it.

但是,如果两个用户同时上传同名文件,就会出现一些问题,你必须处理这个问题。通过读取 responseText,您可以捕获一些错误和异常并对其进行修剪。