«1. Обзор

Библиотека загрузки файлов Apache Commons помогает нам загружать большие файлы по протоколу HTTP, используя тип содержимого multipart/form-data.

В этом кратком руководстве мы рассмотрим, как интегрировать его с Spring.

2. Зависимости Maven

Для использования библиотеки нам понадобится артефакт commons-fileupload:

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

Последнюю версию можно найти на Maven Central.

3. Передача всех сразу

В демонстрационных целях мы собираемся создать контроллер, обрабатывающий запросы с файловой полезной нагрузкой:

@PostMapping("/upload")
public String handleUpload(HttpServletRequest request) throws Exception {
    boolean isMultipart = ServletFileUpload.isMultipartContent(request);

    DiskFileItemFactory factory = new DiskFileItemFactory();
    factory.setRepository(
      new File(System.getProperty("java.io.tmpdir")));
    factory.setSizeThreshold(
      DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD);
    factory.setFileCleaningTracker(null);

    ServletFileUpload upload = new ServletFileUpload(factory);

    List items = upload.parseRequest(request);

    Iterator iter = items.iterator();
    while (iter.hasNext()) {
        FileItem item = iter.next();

        if (!item.isFormField()) {
            try (
              InputStream uploadedStream = item.getInputStream();
              OutputStream out = new FileOutputStream("file.mov");) {

                IOUtils.copy(uploadedStream, out);
            }
        }
    }    
    return "success!";
}

В начале нам нужно проверить, содержит ли запрос составное содержимое с помощью метода isMultipartContent, найденного в классе ServletFileUpload из библиотеки.

По умолчанию в Spring есть MultipartResolver, который нам нужно отключить, чтобы использовать эту библиотеку. В противном случае он прочитает содержимое запроса до того, как оно достигнет нашего контроллера.

Мы можем добиться этого, включив эту конфигурацию в наш файл application.properties:

spring.http.multipart.enabled=false

Теперь мы можем установить каталог, в котором будут сохраняться наши файлы, порог, в котором библиотека решает писать в disk и если файлы должны быть удалены после завершения запроса.

Библиотека предоставляет класс DiskFileItemFactory, который отвечает за настройку сохранения и очистки файла. Метод setRepository задает целевой каталог, в примере показано значение по умолчанию.

Затем setSizeThreshold устанавливает максимальный размер файла.

Затем у нас есть метод setFileCleaningTracker, который при значении null оставляет временные файлы нетронутыми. По умолчанию он удаляет их после завершения запроса.

Теперь мы можем перейти к фактической обработке файлов.

Во-первых, мы создаем наш ServletFileUpload, включая нашу ранее созданную фабрику; затем мы приступаем к разбору запроса и генерации списка FileItem, которые являются основной абстракцией библиотеки для полей формы.

Теперь, если мы знаем, что это не обычное поле формы, мы приступаем к извлечению InputStream и вызову полезного метода копирования из IOUtils (дополнительные параметры вы можете посмотреть в этом руководстве).

Теперь наш файл хранится в нужной папке. Обычно это более удобный способ справиться с этой ситуацией, поскольку он обеспечивает легкий доступ к файлам, но эффективность использования времени/памяти не оптимальна.

В следующем разделе мы рассмотрим потоковый API.

4. Streaming API

Streaming API прост в использовании, что делает его отличным способом обработки больших файлов, просто не копируя их во временное расположение:

ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterStream = upload.getItemIterator(request);
while (iterStream.hasNext()) {
    FileItemStream item = iterStream.next();
    String name = item.getFieldName();
    InputStream stream = item.openStream();
    if (!item.isFormField()) {
        // Process the InputStream
    } else {
        String formFieldValue = Streams.asString(stream);
    }
}

Мы можем видеть в предыдущем фрагменте кода что мы больше не включаем DiskFileItemFactory. Это потому, что при использовании потокового API он нам не нужен.

Далее для обработки полей библиотека предоставляет FileItemIterator, который ничего не читает, пока мы не извлечем их из запроса с помощью метода next.

Наконец, мы можем увидеть, как получить значения других полей формы.

5. Заключение

В этой статье мы рассмотрели, как мы можем использовать библиотеку загрузки файлов Apache Commons с Spring для загрузки и обработки больших файлов.

Как всегда, полный исходный код можно найти на GitHub.