1. アプリケーションクラスを作成する
Spring Boot MVC アプリケーションを開始するには、まずスターターが必要です。この例では、spring-boot-starter thymelaf および spring-boot-starter web が依存関係として追加されています。サーブレット コンテナを使用してファイルをアップロードするには、MultipartConfigElement クラス (web.xml の <multipart-config>) を登録する必要があります。Spring Boot のおかげで、すべてを自動構成できます。
1. 依存関係を導入する
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. ファイルアップロードアプリケーションを作成する
このアプリケーションの使用を開始するために必要なのは、次の UploadingFilesApplication クラス (src/main/java.com/example/uploadingfiles/UploadingFilesApplication.java から) だけです。
package com.example.uploadingfiles;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UploadingFilesApplication {
public static void main(String[] args) {
SpringApplication.run(UploadingFilesApplication.class, args);
}
}
2. ファイルアップロードコントローラーを作成する
元のアプリケーションには、アップロードされたファイルのディスクへの保存とロードを処理するクラスがすでに含まれています。これらはすべて com.example.uploadingfiles.storage パッケージにあります。これらを新しい FileUploadController で使用します。次のリスト (src/main/java.com/example/uploadingfiles/FileUploadController.java から) は、ファイル アップロード コントローラーを示しています。
package com.example.uploadingfiles;
import java.io.IOException;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.example.uploadingfiles.storage.StorageFileNotFoundException;
import com.example.uploadingfiles.storage.StorageService;
@Controller
public class FileUploadController {
private final StorageService storageService;
@Autowired
public FileUploadController(StorageService storageService) {
this.storageService = storageService;
}
@GetMapping("/")
public String listUploadedFiles(Model model) throws IOException {
model.addAttribute("files", storageService.loadAll().map(
path -> MvcUriComponentsBuilder.fromMethodName(FileUploadController.class,
"serveFile", path.getFileName().toString()).build().toUri().toString())
.collect(Collectors.toList()));
return "uploadForm";
}
@GetMapping("/files/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
@PostMapping("/")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
storageService.store(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "redirect:/";
}
@ExceptionHandler(StorageFileNotFoundException.class)
public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
return ResponseEntity.notFound().build();
}
}
FileUploadController クラスには @Controller アノテーションが付けられているため、Spring MVC がクラスを取得してルートを見つけることができます。各メソッドは @GetMapping または @PostMapping でマークされ、パスと HTTP アクションを特定のコントローラー アクションに関連付けます。
この場合:
-
GET /: 現在アップロードされているファイルのリストを StorageService から検索し、Thymelaf テンプレートにロードします。MvcUriComponentsBuilder を使用して、実際のリソースへのリンクを計算します。
-
GET /files/{filename}: リソース (存在する場合) をロードし、Content-Disposition 応答ヘッダーを使用してダウンロードのためにブラウザに送信します。
-
POST/: マルチパート メッセージ ファイルを処理し、それを保存するために StorageService に提供します。
コントローラーがファイル システムなどのストレージ層と対話できるように、StorageService を提供する必要があります。次のリスト (src/main/java.com/example/uploadingfiles/storage/StorageService.java から) はインターフェイスを示しています。
package com.example.uploadingfiles.storage;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Path;
import java.util.stream.Stream;
public interface StorageService {
void init();
void store(MultipartFile file);
Stream<Path> loadAll();
Path load(String filename);
Resource loadAsResource(String filename);
void deleteAll();
}
3. HTMLテンプレートを作成する
次の Thymelaf テンプレート (src/main/resources/templates/uploadForm.html から) は、ファイルをアップロードしてアップロードされたコンテンツを表示する方法の例を示しています。
<html xmlns:th="https://www.thymeleaf.org">
<body>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div>
<form method="POST" enctype="multipart/form-data" action="/">
<table>
<tr><td>File to upload:</td><td><input type="file" name="file" /></td></tr>
<tr><td></td><td><input type="submit" value="Upload" /></td></tr>
</table>
</form>
</div>
<div>
<ul>
<li th:each="file : ${files}">
<a th:href="${file}" th:text="${file}" />
</li>
</ul>
</div>
</body>
</html>
このテンプレートは次の 3 つの部分で構成されます。
-
上部のオプションのメッセージ。Spring MVC はフラッシュ スコープのメッセージを書き込みます。
-
ユーザーがファイルをアップロードできるフォーム。
-
バックエンドから提供されるファイルのリスト。
4. ファイルのアップロード制限を調整する
ファイルのアップロードを構成する場合、ファイル サイズ制限を設定すると便利なことがよくあります。5 GB のファイルのアップロードを処理しようとしているところを想像してみてください。Spring Boot を使用すると、いくつかのプロパティ設定を通じて、自動構成された MultipartConfigElement を調整できます。
次のプロパティを既存のプロパティ セット (src/main/resources/application.nenenebc プロパティ内) に追加します。
spring.servlet.multipart.max-file-size=128KB
spring.servlet.multipart.max-request-size=128KB
マルチパート設定の制約は次のとおりです。
-
spring.servlet.multipart.max-file-size は 128KB に設定されており、ファイルの合計サイズが 128KB を超えることができないことを示しています。
-
spring.servlet.multipart.max-request-size は 128KB に設定されています。これは、リクエストの合計サイズが 128KB を超えることはできないことを意味します。マルチパート/フォームデータ
5. アプリケーションを実行する
アップロードされたファイルの宛先フォルダーが必要なため、Spring Initializer によって作成された基本の UploadingFilesApplication クラスを拡張し、起動時にフォルダーを削除して再作成する Boot CommandLineRunner を追加する必要があります。次のリスト (src/main/java.com/example/uploadingfiles/UploadingFilesApplication.java から) は、これを行う方法を示しています。
package com.example.uploadingfiles;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import com.example.uploadingfiles.storage.StorageProperties;
import com.example.uploadingfiles.storage.StorageService;
@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class UploadingFilesApplication {
public static void main(String[] args) {
SpringApplication.run(UploadingFilesApplication.class, args);
}
@Bean
CommandLineRunner init(StorageService storageService) {
return (args) -> {
storageService.deleteAll();
storageService.init();
};
}
}
@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。
@Configuration: クラスをアプリケーション コンテキストの Bean 定義のソースとしてマークします。
@EnableAutoConfiguration: クラスパス設定、他の Bean、およびさまざまなプロパティ設定に基づいて Bean の追加を開始するように SpringBoot に指示します。たとえば、spring-webmvc がクラスパス上にある場合、このアノテーションはアプリケーションを Web アプリケーションとしてマークし、DispatcherServlet のセットアップなどの主要な動作をアクティブ化します。
@ComponentScan: com/example パッケージ内の他のコンポーネント、構成、サービスを検索するように Spring に指示し、コントローラーを見つけさせます。
main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。XML が 1 行も含まれていないことに気づきましたか? web.xml ファイルもありません。この Web アプリケーションは 100% Pure Java であり、配管やインフラストラクチャを構成する必要はありません。
6. テスト
アプリケーションでこの特定の機能をテストするには、さまざまな方法があります。次のリスト (src/test/java.com/example/uploadingfiles/FileUploadTests.java から) は、サーブレット コンテナーを起動する必要がないように MockMvc を使用する例を示しています。
package com.example.uploadingfiles;
import java.nio.file.Paths;
import java.util.stream.Stream;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.example.uploadingfiles.storage.StorageFileNotFoundException;
import com.example.uploadingfiles.storage.StorageService;
@AutoConfigureMockMvc
@SpringBootTest
public class FileUploadTests {
@Autowired
private MockMvc mvc;
@MockBean
private StorageService storageService;
@Test
public void shouldListAllFiles() throws Exception {
given(this.storageService.loadAll())
.willReturn(Stream.of(Paths.get("first.txt"), Paths.get("second.txt")));
this.mvc.perform(get("/")).andExpect(status().isOk())
.andExpect(model().attribute("files",
Matchers.contains("http://localhost/files/first.txt",
"http://localhost/files/second.txt")));
}
@Test
public void shouldSaveUploadedFile() throws Exception {
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt",
"text/plain", "Spring Framework".getBytes());
this.mvc.perform(multipart("/").file(multipartFile))
.andExpect(status().isFound())
.andExpect(header().string("Location", "/"));
then(this.storageService).should().store(multipartFile);
}
@SuppressWarnings("unchecked")
@Test
public void should404WhenMissingFile() throws Exception {
given(this.storageService.loadAsResource("test.txt"))
.willThrow(StorageFileNotFoundException.class);
this.mvc.perform(get("/files/test.txt")).andExpect(status().isNotFound());
}
}