Загрузка файлов – очень распространенная задача в любом веб-приложении. Мы уже видели, как загружать файлы в Servlet и загружать файлы в Struts2. Сегодня мы узнаем о загрузке файлов в Spring, в частности о загрузке файлов в Spring MVC для одного и нескольких файлов.
Загрузка файлов в Spring MVC
Фреймворк Spring MVC предоставляет поддержку для загрузки файлов путем интеграции с API Apache Commons FileUpload. Процесс загрузки файлов очень прост и требует простых настроек. Мы создадим простой проект Spring MVC в STS, который будет выглядеть примерно как на изображении ниже. Большая часть кода состоит из шаблонного кода, сгенерированного средством STS, мы сосредоточимся на изменениях, необходимых для использования интеграции загрузки файлов Spring.
Зависимости Maven для Apache Commons FileUpload
Прежде всего, нам нужно добавить зависимости Apache Commons FileUpload в наш файл pom.xml, чтобы необходимые jar-файлы стали частью веб-приложения. Ниже приведен фрагмент зависимости из моего файла pom.xml.
<!-- Apache Commons FileUpload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- Apache Commons IO -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
Представления формы загрузки файла Spring
Мы создадим две JSP-страницы для разрешения загрузки одного и нескольких файлов в веб-приложении Spring. Код представления upload.jsp:
<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Upload File Request Page</title>
</head>
<body>
<form method="POST" action="uploadFile" enctype="multipart/form-data">
File to upload: <input type="file" name="file"><br />
Name: <input type="text" name="name"><br /> <br />
<input type="submit" value="Upload"> Press here to upload the file!
</form>
</body>
</html>
Код представления uploadMultiple.jsp:
<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Upload Multiple File Request Page</title>
</head>
<body>
<form method="POST" action="uploadMultipleFile" enctype="multipart/form-data">
File1 to upload: <input type="file" name="file"><br />
Name1: <input type="text" name="name"><br /> <br />
File2 to upload: <input type="file" name="file"><br />
Name2: <input type="text" name="name"><br /> <br />
<input type="submit" value="Upload"> Press here to upload the file!
</form>
</body>
</html>
Обратите внимание, что эти файлы являются простыми HTML-файлами, я не использую никакие теги JSP или Spring для избежания сложности. Важный момент: атрибут enctype формы должен быть multipart/form-data, чтобы веб-приложение Spring знало, что запрос содержит данные файла, которые нужно обработать. Также обратите внимание, что для нескольких файлов поля формы “file” и “name” совпадают в полях ввода, чтобы данные были отправлены в виде массива. Мы возьмем входной массив, разберем данные файла и сохраним их с указанным именем файла.
Настройка многокомпонентной конфигурации Spring MVC
Для использования Apache Commons FileUpload для обработки многокомпонентных запросов нам просто нужно настроить бин multipartResolver
с классом org.springframework.web.multipart.commons.CommonsMultipartResolver
. Наш конечный файл конфигурации Spring выглядит следующим образом. Код servlet-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:beans="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/**" location="/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- setting maximum upload size -->
<beans:property name="maxUploadSize" value="100000" />
</beans:bean>
<context:component-scan base-package="com.journaldev.spring.controller" />
</beans:beans>
Обратите внимание, что я устанавливаю максимальный размер загружаемого файла, предоставляя значение свойства maxUploadSize для бина multipartResolver. Если вы посмотрите в исходный код класса DispatcherServlet
, вы увидите, что переменная MultipartResolver с именем multipartResolver определена и инициализирована в следующем методе.
private void initMultipartResolver(ApplicationContext context)
{
try
{
this.multipartResolver = ((MultipartResolver)context.getBean("multipartResolver", MultipartResolver.class));
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex)
{
this.multipartResolver = null;
if (this.logger.isDebugEnabled())
this.logger.debug("Unable to locate MultipartResolver with name 'multipartResolver': no multipart request handling provided");
}
}
С этой конфигурацией любой запрос с enctype как multipart/form-data будет обрабатываться multipartResolver перед передачей в класс контроллера.
Класс контроллера загрузки файла Spring
Класс контроллера очень простой, нам нужно определить методы обработчика для URI uploadFile и uploadMultipleFile. Код FileUploadController.java:
package com.journaldev.spring.controller;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
/**
* Handles requests for the application file upload requests
*/
@Controller
public class FileUploadController {
private static final Logger logger = LoggerFactory
.getLogger(FileUploadController.class);
/**
* Upload single file using Spring Controller
*/
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public @ResponseBody
String uploadFileHandler(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
// Создание каталога для хранения файла
String rootPath = System.getProperty("catalina.home");
File dir = new File(rootPath + File.separator + "tmpFiles");
if (!dir.exists())
dir.mkdirs();
// Создание файла на сервере
File serverFile = new File(dir.getAbsolutePath()
+ File.separator + name);
BufferedOutputStream stream = new BufferedOutputStream(
new FileOutputStream(serverFile));
stream.write(bytes);
stream.close();
logger.info("Server File Location="
+ serverFile.getAbsolutePath());
return "You successfully uploaded file=" + name;
} catch (Exception e) {
return "You failed to upload " + name + " => " + e.getMessage();
}
} else {
return "You failed to upload " + name
+ " because the file was empty.";
}
}
/**
* Upload multiple file using Spring Controller
*/
@RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST)
public @ResponseBody
String uploadMultipleFileHandler(@RequestParam("name") String[] names,
@RequestParam("file") MultipartFile[] files) {
if (files.length != names.length)
return "Mandatory information missing";
String message = "";
for (int i = 0; i < files.length; i++) {
MultipartFile file = files[i];
String name = names[i];
try {
byte[] bytes = file.getBytes();
// Создание каталога для хранения файла
String rootPath = System.getProperty("catalina.home");
File dir = new File(rootPath + File.separator + "tmpFiles");
if (!dir.exists())
dir.mkdirs();
// Создание файла на сервере
File serverFile = new File(dir.getAbsolutePath()
+ File.separator + name);
BufferedOutputStream stream = new BufferedOutputStream(
new FileOutputStream(serverFile));
stream.write(bytes);
stream.close();
logger.info("Server File Location="
+ serverFile.getAbsolutePath());
message = message + "You successfully uploaded file=" + name
+ "
";
} catch (Exception e) {
return "You failed to upload " + name + " => " + e.getMessage();
}
}
return message;
}
}
Обратите внимание на использование аннотаций Spring, которые упрощают нашу жизнь и делают код более читабельным. Метод uploadFileHandler
используется для обработки сценария загрузки одного файла, тогда как метод uploadMultipleFileHandler
используется для обработки сценария загрузки нескольких файлов. Фактически, мы могли бы использовать один метод для обработки обоих сценариев. Теперь экспортируйте приложение как файл WAR и разверните его в контейнере сервлетов Tomcat. При запуске нашего приложения ниже приведены изображения запросов и ответов.
Пример загрузки файла в Spring MVC
Вы можете проверить журналы сервера, чтобы узнать местоположение, где были сохранены файлы. Загрузите проект по ссылке выше и поиграйтесь с ним, чтобы узнать больше.