【问题标题】:How to upload large files using MVC 4?如何使用 MVC 4 上传大文件?
【发布时间】:2013-04-23 15:41:48
【问题描述】:

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

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

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

这是我当前的代码

  $(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: '' }
    });

和控制器

  [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));
        }

【问题讨论】:

  • 一个 4MB 的文件应该能够在 ASP.NET 中不进行分块的情况下进行处理。您可能需要增加最大上传文件大小,或者增加执行时间。
  • 我上传的最大文件是 7268k。我需要分块吗?或者我要改变什么?
  • 更改 web.config 中的 maxRequestLength。查看stackoverflow.com/a/288675/254973 以了解您需要在那里进行更改的示例。之后,从 javascript 中删除 chunk_size 选项,这样 plUpload 就不会发送分块上传。
  • 如果您的文件大于 1mb,则应该启用分块,我倾向于坚持使用 500kb 的块。尽管服务器通常接受 4mb 块,但对于互联网速度较慢的人来说,可能会发生超时。所以它不是真正的比例。分块允许速率限制,恢复上传。您甚至可以检查文件大小并根据文件大小和速度调整块大小。更改帖子大小值不好的做法,它是如此 Apache 和 1990。您需要设置客户端脚本也启用分块,使用 HTML5 或其他后备。不要发布整个大文件!
  • jschunk_size: '4mb', and max_file_size: '20000mb',这几行是什么意思??当块大小为 4mb 时会发生什么?

标签: c# jquery asp.net-mvc file-upload plupload


【解决方案1】:

在 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>

【讨论】:

  • 谢谢谢恩!这适用于我的“Intranet”应用程序,但我使用的是 targetFramework="4.0" 所以我的没有 部分。我最终限制在 300MB 到 700MB 之间。我还没有完全测试找到确切的限制。
  • 我认为您的意思是 而不是 。可能不同的版本不一样。
  • 对于一个网站,它是 system.web
  • 编译和 httpRuntime 进入 system.web - 安全进入 system.webserver。 maxRequestLength 也以千字节为单位,而 maxAllowedContentLenght 和 requestLengthDiskThreshold 以字节为单位
  • 这太糟糕了!如果我的文件在 1.8GB 时失败会发生什么...重新启动。另外,它对 DDoS 攻击开放,启动 100+ 次(使用客户端脚本可以很容易地在一秒钟内启动 10000 次上传)上传,然后你的服务器就会变砖!不要这样做!实施适当的分块。这也允许您限制速率,以及恢复上传。 -1
【解决方案2】:

当前版本

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

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

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

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

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

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

<httpRuntime maxRequestLength="20971520000" />

在 configuration/system.web/httpRuntime 标签树中。再一次,以防您无法理解它的插入位置,完整的代码块如下所示:

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

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

【讨论】:

    【解决方案3】:

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

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

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

    然后分块文件,并发送每个卡盘,等待响应并发送下一个块。这是 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 代码:

            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);
            });
    

    这里是上传控制器,用于将 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);
            }
        }
    }
    

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

    【讨论】:

    • 工作得很好!顺便说一句,这里的递归可能会限制。每个浏览器都有自己的限制。
    • (y) 这对我很有帮助,谢谢
    猜你喜欢
    • 2014-08-22
    • 2013-12-23
    • 2020-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    • 2015-10-28
    相关资源
    最近更新 更多