[Spring MVC]ファイルのダウンロード
31448 ワード
1. Intro
Springからファイルをダウンロードする方法を見てみましょう.
2.開発環境
3.ファイルのダウンロード
ResponseEntity Resourceの使用
@Controller
public class DownloadController {
private static final String SAMPLE_FILE_NAME = "스프링.png"; // (1)
@Value("classpath:static/spring.png") // (2)
private Resource resource;
@GetMapping("/download/img")
public ResponseEntity<Resource> downloadImg() {
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG) // (3)
.header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.inline() // (4)
.filename(SAMPLE_FILE_NAME, StandardCharsets.UTF_8)
.build()
.toString())
.body(resource);
}
@GetMapping("/download/file")
public ResponseEntity<Resource> downloadFile() {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM) // (5)
.header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment() // (6)
.filename(SAMPLE_FILE_NAME, StandardCharsets.UTF_8)
.build()
.toString())
.body(resource);
}
}
共通
ブラウザにすぐに表示されるapi(/ダウンロード/img)
ファイルダウンロードapi(/ダウンロード/file)
テストコード
@WebMvcTest(DownloadController.class)
@Slf4j
class DownloadControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void downloadImg() throws Exception {
// when
MvcResult mvcResult = mockMvc.perform(get("/download/img"))
.andExpect(status().isOk())
.andReturn();
// then
MockHttpServletResponse response = mvcResult.getResponse();
int contentLength = response.getContentLength();
String contentType = response.getContentType();
String contentDisposition = response.getHeader(HttpHeaders.CONTENT_DISPOSITION);
assertAll(
() -> assertThat(contentLength).isEqualTo(9183),
() -> assertThat(contentType).isEqualTo(MediaType.IMAGE_PNG_VALUE),
() -> assertThat(contentDisposition).contains("inline", "UTF-8")
);
// inline; filename*=UTF-8''%EC%8A%A4%ED%94%84%EB%A7%81.png
log.info("contentDisposition : {}", contentDisposition);
}
@Test
void downloadFile() throws Exception {
// when
MvcResult mvcResult = mockMvc.perform(get("/download/file"))
.andExpect(status().isOk())
.andReturn();
// then
MockHttpServletResponse response = mvcResult.getResponse();
int contentLength = response.getContentLength();
String contentType = response.getContentType();
String contentDisposition = response.getHeader(HttpHeaders.CONTENT_DISPOSITION);
assertAll(
() -> assertThat(contentLength).isEqualTo(9183),
() -> assertThat(contentType).isEqualTo(MediaType.APPLICATION_OCTET_STREAM_VALUE),
() -> assertThat(contentDisposition).contains("attachment", "UTF-8")
);
// attachment; filename*=UTF-8''%EC%8A%A4%ED%94%84%EB%A7%81.png
log.info("contentDisposition : {}", contentDisposition);
}
}
Content-Length Header
コントローラからResponseEntityに戻ると、
HttpEntityMethodProcessor
はHandleReturnValueで処理されます.AbstractMessageConverterMethodProcessor
WriteWithMessageConverterからメッセージ変換器を選択して処理します.選択されたメッセージ変換器は
ResourceHttpMessageConverter
です.親
AbstractHttpMessageConverter
の実際の書き込み方法では、211行のaddDefaultHeadersが表示されます.259行に実コンテンツ長が設定されていない場合は、自動的に追加されます.
したがって、コントローラにタイトルを個別に設定する必要はありません.
ファイルのダウンロード時にハングルファイル名を破壊
従来のブラウザ固有のハングル文字の処理
複数の既存の例を表示する場合は、次の例を参照してください.
ファイル名をユーザーエージェントとしてブランチ処理し、ブラウザでエンコードすることがよく見られます.
public String getBrowser(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
if (userAgent.contains("MSIE") || userAgent.contains("Trident") || userAgent.contains("Edge")) {
return "MSIE";
} else if (userAgent.contains("Chrome")) {
return "Chrome";
} else if (userAgent.contains("Opera")) {
return "Opera";
} else if (userAgent.contains("Safari")) {
return "Safari";
} else if (userAgent.contains("Firefox")) {
return "Firefox";
} else {
return "";
}
}
public String encodeFileName(String browser, String filename) {
if ("MSIE".equals(browser)) {
return URLEncoder.encode(filename, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
}
if ("Firefox".equals(browser)) {
return // ...
}
if ("Chrome".equals(browser)) {
return // ...
}
...
}
RFC 6266,RFC 5987
異なるブラウザはRFC6266とRFC5987の仕様をサポートしています.
したがって、説明を使用して「コンテンツ-場所」を設定すると、一度に完了できます.
http://test.greenbytes.de/tech/tc2231.
IEはIE 9からサポートを開始する.
TestResultsFF22passMSIE8unsupportedMSIE9passOperapassSaf6passKonqpassChr25pass
Spring ContentDisposition Class
Spring
はまた、使いやすいクラスの処理をサポートする.ContentDisposition
でfilenameにcharsetが指定されている場合、タイトルは自動的に対応するspecに符号化されます.@Slf4j
class ContentDispositionTest {
@ParameterizedTest
@CsvSource({
"스프링.png, inline; filename*=UTF-8''%EC%8A%A4%ED%94%84%EB%A7%81.png",
"스프링1234.png, inline; filename*=UTF-8''%EC%8A%A4%ED%94%84%EB%A7%811234.png",
"스프링-!@#$%.png, inline; filename*=UTF-8''%EC%8A%A4%ED%94%84%EB%A7%81-!%40#$%25.png"
})
void buildContentDisposition(String filename, String expected) {
// when
ContentDisposition contentDisposition = ContentDisposition.inline()
.filename(filename, StandardCharsets.UTF_8)
.build();
// then
assertThat(contentDisposition.toString()).isEqualTo(expected);
}
}
コンテンツ→位置見出し解析
Content-Disposition: inline; filename*=UTF-8''%EC%8A%A4%ED%94%84%EB%A7%81.png
RFC2231の4.Parameter Value Character SetおよびLanguage Informationを参照してください.
パラメータ値に文字セットまたは言語を指定する必要がある場合は、アステリーリスク(*)を加え、
'
を区切り記号とします.文字セット、言語、値の順に設定すればいいです.
最後に、文字セットまたは言語は空にすることができますが、
'
を表示する必要があります.4.終了
ブログで使用されているコードはGithubで見つけることができます.
5.「」を参照してください。
Reference
この問題について([Spring MVC]ファイルのダウンロード), 我々は、より多くの情報をここで見つけました https://velog.io/@csh0034/Spring-MVC-파일-다운로드テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol