Когда передо мной встала задачка организовать красивый и удобный механизм загрузки файлов на сервер с поддержкой загрузки больших файлов, отображения прогресса загрузки и прочих радостей современного веба, я стал искать, что же есть в наличии такого, чтобы не ваять очередной велосипед.
С Javascript’овыми аплоадерами все оказалось очень неплохо – вариантов весьма приличное количество, на все требования и вкусы. А вот с серверными компонентами для реализации бекенда ситуация оказалась несколько хуже. Большая часть решений, которые я нашел, представляла собой небольшие примеры хэндлеров, об которые для реального использования пришлось бы сточить не один напильник.
В итоге, для работы на клиенте был выбран неоднократно обсуждавшийся на хабре jQuery-File-Upload, благо для него есть и angularjs обертка (что было актуально), и, в его документации была обнаружена ссылка на ту реализацию бекенда, о которой я и хотел бы рассказать – Backload.
Backload представляет собой полноценную реализацию бекенда для нескольких js-плагинов для upload’а (включая jquery-file-upload). Проект постоянно развивается, имеет приличный набор настроек и возможностей и позволяет быстро поднять толково работающий механизм для приема загружаемых файлов.
Проект имеет несколько редакций – бесплатную (standart) и ряд платных. Но данная ложка дегтя компенсируется тем, что для большинства задач бесплатного варианта хватает с головой (мне хватило полностью).
Для начала работы достаточно установить пакет Backload из nuget’а или поставить его сразу с jquery-file-upload’ом.
Для самых простых целей – простой установки хватает, достаточно лишь указать для jquery-file-upload нужный URL:
var fileUoloadUrl = "/Backload/UploadHandler";
$('#fileupload').fileupload({
url: fileUploadUrl
});
И все. Запросы будут обрабатываться встроенным обработчиком.
Для ситуаций, когда этого недостаточно и хочется настроить и кастомизировать процесс более детально – имеется возможность настроить поведение Backload’а с помощью правки конфигурационного файла (.config), либо же влезть в работу обработчика, обрабатывая его события.
Для этого надо создать свой контроллер, и указать jquery-file-upload’у его урл. После чего в своем контроллере создаем экземпляр FileUploadHandler, подписываемся на те из его событий, которые нам нужны и передаем ему обработку.
public class FileUploadController : Controller
{
public async Task<ActionResult> FileHandler()
{
FileUploadHandler handler = new FileUploadHandler(Request, this);
handler.IncomingRequestStarted += handler_IncomingRequestStarted;
handler.AuthorizeRequestStarted += handler_AuthorizeRequestStarted;
handler.AuthorizeRequestFinished += handler_AuthorizeRequestFinished;
handler.GetFilesRequestStarted += handler_GetFilesRequestStarted;
handler.GetFilesRequestFinished += handler_GetFilesRequestFinished;
handler.GetFilesRequestException += handler_GetFilesRequestException;
handler.StoreFileRequestStartedAsync += handler_StoreFileRequestStartedAsync
handler.StoreFileRequestFinished += handler_StoreFileRequestFinished;
handler.StoreFileRequestException += handler_StoreFileRequestException;
handler.DeleteFilesRequestStarted += handler_DeleteFilesRequestStarted;
handler.DeleteFilesRequestFinishedAsync += handler_DeleteFilesRequestFinishedAsync;
handler.DeleteFilesRequestException += handler_DeleteFilesRequestException;
handler.OutgoingResponseCreated += handler_OutgoingResponseCreated;
handler.ProcessPipelineExceptionOccured += handler_ProcessPipelineExceptionOccured;
ActionResult result = await handler.HandleRequestAsync();
return result;
}
/* обработка событий*/
}
После этого становится возможным влиять на процесс обработки запроса аплоада файла.
Например, по какому-то условию менять имя файла, путь для сохранения или просто отказывать в аплоаде по каким-либо критериям.
async Task handler_StoreFileRequestStartedAsync(object sender, StoreFileRequestEventArgs e)
{
var fileName = e.Param.FileStatusItem.FileName;
if (fileName.Equals("some_bad_name.tmp", StringComparison.OrdinalIgnoreCase))
{
fileName = "some_good_name.tmp";
e.Param.FileStatusItem.FileName = fileName;
e.Param.FileStatusItem.UpdateStatus(true);
}
}
Или, например, по завершении аплоада что-то сделать с файлом.
void handler_StoreFileRequestFinished(object sender, StoreFileRequestEventArgs e)
{
var fileName = e.Param.FileStatusItem.FileName;
var folder = e.Param.FileStatusItem.StorageInfo.FileDirectory;
DoSomeOperations(folder, fileName);
}
По данным событиям есть весьма подробное описание в документации и даже примеры их использования. Спектр событий достаточно велик для того, чтобы вмешиваться в процесс именно тогда, когда нужно. В частности, мне очень удобно оказалось реализовать довольно непростую логику определения нужного пути для загрузки файлов, проверки прав на аплоад файлов в конкретные папки, свое ведение лога операций с файлами и добавления к загружаемым файлам файлов с метаданными.
Надеюсь данная статья будет кому-либо полезной и покажет, что есть вполне простые, но удобные решения для работы с загружаемыми файлами.
Спасибо за внимание!
Автор: Almazen