В статье Загрузка файлов на сервер в ASP.NET MVC были рассмотрены общие моменты оптравки файлов на сервер в обычном запросе POST. Но если мы используем Ajax-запросы, то загрузка файлов будет иметь свои особенности.
Сначала определим метод в контроллере MVC :
[HttpPost] public JsonResult Upload() { foreach (string file in Request.Files) { var upload = Request.Files[file]; if (upload != null) { // получаем имя файла string fileName = System.IO.Path.GetFileName(upload.FileName); // сохраняем файл в папку Files в проекте upload.SaveAs(Server.MapPath("~/Files/" + fileName)); } } return Json("файл загружен"); }
Здесь предполагается, что у нас в проекте определена папка Files для хранения загруженных файлов. Для получения файлов используется коллекция Request.Files
После сохранения файла пользователю отдается результат в виде строки.
Код представления тогда будет выглядеть следующим образом:
@{ ViewBag.Title = "Home Page"; } <div> <input type="file" name="upload" id="uploadFile" /><br /> <button id="submit">Загрузить</button> </div> @section scripts{ <script type="text/javascript"> $('#submit').on('click', function (e) { e.preventDefault(); var files = document.getElementById('uploadFile').files; if (files.length > 0) { if (window.FormData !== undefined) { var data = new FormData(); for (var x = 0; x < files.length; x++) { data.append("file" + x, files[x]); } $.ajax({ type: "POST", url: '@Url.Action("Upload", "Home")', contentType: false, processData: false, data: data, success: function (result) { alert(result); }, error: function (xhr, status, p3) { alert(xhr.responseText); } }); } else { alert("Браузер не поддерживает загрузку файлов HTML5!"); } } }); </script> }
Нам не нужна стандартная форма, все делается через ajax. Сначала получаем все выбранные файлы:
var files = document.getElementById('uploadFile').files
Затем формируем объект FormData
, в который добавляем все выбранные файлы:
var data = new FormData(); for (var x = 0; x < files.length; x++) { data.append("file" + x, files[x]); }
И отсылаем их на сервер.
В Web API контроллер будет выглядеть следующим образом:
public class ValuesController : ApiController { public async Task<IHttpActionResult> Post() { if (!Request.Content.IsMimeMultipartContent()) { return BadRequest(); } var provider = new MultipartMemoryStreamProvider(); // путь к папке на сервере string root = System.Web.HttpContext.Current.Server.MapPath("~/Files/"); await Request.Content.ReadAsMultipartAsync(provider); foreach (var file in provider.Contents) { var filename = file.Headers.ContentDisposition.FileName.Trim('\"'); byte[] fileArray = await file.ReadAsByteArrayAsync(); using (System.IO.FileStream fs = new System.IO.FileStream(root + filename, System.IO.FileMode.Create)) { await fs.WriteAsync(fileArray, 0, fileArray.Length); } } return Ok("файлы загружены"); } }
Здесь используется асинхронная обработка запроса. Вначале метод IsMultipartContent() проверяет, содержит ли запрос корректные данные. Если нет, то возвращаем статусный код 401.
Для асинхронного чтения с потока создается провайдер:
var provider = new MultipartMemoryStreamProvider(); Request.Content.ReadAsMultipartAsync(provider);
После считывания потока свойство provider.Contents
будет содержать все считанные значения, в том числе и файлы. И в цикле проходим
по всем файлам и читаем их в массив байтов:
byte[] fileArray = await file.ReadAsByteArrayAsync();
Затем с помощью объекта FileStream считанный массив сохраняется на диске в папке проекта.
Представление будет тем же, что и для MVC, за исключением url запроса:
$.ajax({ type: "POST", url: 'api/values/post', contentType: false, processData: false, data: data, success: function (result) { alert(result); }, error: function (xhr, status, p3) { alert(status); } });