Как загрузить большие файлы с помощью MVC 4?
У меня это работало... но я заметил, что как только файлы, которые я загружаю, становятся больше (около 4000 КБ), контроллер не будет вызываться..
Поэтому я добавил в чанкинг, который исправил эту проблему... но теперь, когда я открываю файл, в котором полно мусорных символов...
Итак, как правильно загружать большие файлы с помощью 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));
}
3 ответа
В web.config вам нужны эти (2 Гб вокруг):
<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>
У меня была эта проблема, и я нашел решение на основе кода Джонатана здесь. Если вы хотите загрузить большой файл, например, видеофайл размером 1 Гб, вам нужно собрать файл и отправить его через несколько запросов (один запрос дает время ожидания). сначала вы устанавливаете максимальный предел для стороны клиента и сервера в 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, вы можете поймать некоторую ошибку и исключение и обрезать его.
Текущая версия
В соответствии с подробным описанием ошибки IIS 8.0, которая является версией, которую я использовал во время написания этого ответа, необходимо проверить https://www.iis.net/configreference/system.webserver/security/requestfiltering/requestlimits @maxAllowedContentLength в ApplicationHost.config или в Интернете. Файл.config. Это означает, что вам нужно включить:
<requestLimits maxAllowedContentLength="20971520000"></requestLimits>
внутри дерева тегов конфигурации /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, все еще могут искать эту конфигурацию через конфигурацию /system.web/httpRuntime @maxRequestLength, но, как видно из связанной страницы, это более недоступен, хотя класс HttpRuntime, который не применим к этому сценарию, все еще существует с.Net Framework 1.1. Если это так, вам необходимо включить:
<httpRuntime maxRequestLength="20971520000" />
внутри дерева тегов конфигурации /system.web/httpRuntime. Еще раз, на случай, если вам не хватает понимания, чтобы выяснить, куда он вставляется, полный блок кода выглядит примерно так:
<configuration>
<system.web>
<httpRuntime maxRequestLength="20971520000" />
</system.web>
</configuration>
Номер размера файла - это просто произвольное число (20000 МБ, а не 20 ГБ, что, скорее всего, составляет 21 474 836 480) для демонстрации. Если вы не программируете веб-сайт для узкой группы безопасности, которой необходимо загружать большие файлы, вы не должны позволять загружать файлы такого большого размера на ваш веб-сервер.