Java/Spring:如何在不使用 InputStream 的情况下找出 MimeType
Java/Spring: How to Figure out MimeType on an InputStream Without Consuming It
基础知识
这是一个 Java 1.8
Spring Boot 1.5
应用程序。
它目前使用 Apache Tika 1.22
读取 Mime-Type 信息,但这很容易更改。
摘要
有一个用户用来下载文件的映射器。这些文件来自与应用程序分开的另一个 URL
。该文件可能是多种类型(excel
、PDF
、text
等),应用程序在将文件拉下来之前无法知道它是什么。
问题
为了 return 将具有适当标题、扩展名和 ContentType
的文件下载给用户,应用程序使用 Apache Tika
提取该信息。不幸的是,现在 InputStream
的 header 被消耗了,当应用程序将 InputStream
写入 HttpServletResponse
时,文件不完整。
这意味着,为了当前运行,应用程序关闭第一个 InputStream
,然后向用户打开第二个 InputStream
到 return。
这不好,因为这意味着 URL
被调用了两次,浪费了系统资源。
使用此功能的正确方法是什么?
代码示例
@GetMapping("/My/Download/")
public void doDownload(HttpServletResponse httpServletResponse) {
String externalFileURL = "http://www.pdf995.com/samples/pdf.pdf";
try {
InputStream firstStream = new URL(externalFileURL).openStream();
TikaConfig tikaConfig = new TikaConfig();
MediaType mediaType = tikaConfig.getDetector().detect(TikaInputStream.get(firstStream), new Metadata());
firstStream.close();
InputStream secondStream = new URL(externalFileURL).openStream();
httpServletResponse.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", "DownloadMe." + mediaType.getSubtype()));
httpServletResponse.setContentType(mediaType.getBaseType().toString());
FileCopyUtils.copy(secondStream, httpServletResponse.getOutputStream());
httpServletResponse.flushBuffer();
} catch (Exception e) {
}
}
detect()
的 Javadoc 说:
The given stream is guaranteed to support the mark feature
and the detector is expected to mark
the stream before reading any bytes from it, and to reset
the stream before returning.
TikaInputStream
的 Javadoc 说:
The created TikaInputStream instance keeps track of the original resource used to create it, while behaving otherwise just like a normal, buffered InputStream
. A TikaInputStream instance is also guaranteed to support the mark(int)
feature.
也就是说你应该使用TikaInputStream
来阅读内容,然后用try-with-resources来关闭它:
try (InputStream tikaStream = TikaInputStream.get(new URL(externalFileURL))) {
TikaConfig tikaConfig = new TikaConfig();
MediaType mediaType = tikaConfig.getDetector().detect(tikaStream, new Metadata());
httpServletResponse.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", "DownloadMe." + mediaType.getSubtype()));
httpServletResponse.setContentType(mediaType.getBaseType().toString());
FileCopyUtils.copy(tikaStream, httpServletResponse.getOutputStream());
httpServletResponse.flushBuffer();
}
基础知识
这是一个 Java 1.8
Spring Boot 1.5
应用程序。
它目前使用 Apache Tika 1.22
读取 Mime-Type 信息,但这很容易更改。
摘要
有一个用户用来下载文件的映射器。这些文件来自与应用程序分开的另一个 URL
。该文件可能是多种类型(excel
、PDF
、text
等),应用程序在将文件拉下来之前无法知道它是什么。
问题
为了 return 将具有适当标题、扩展名和 ContentType
的文件下载给用户,应用程序使用 Apache Tika
提取该信息。不幸的是,现在 InputStream
的 header 被消耗了,当应用程序将 InputStream
写入 HttpServletResponse
时,文件不完整。
这意味着,为了当前运行,应用程序关闭第一个 InputStream
,然后向用户打开第二个 InputStream
到 return。
这不好,因为这意味着 URL
被调用了两次,浪费了系统资源。
使用此功能的正确方法是什么?
代码示例
@GetMapping("/My/Download/")
public void doDownload(HttpServletResponse httpServletResponse) {
String externalFileURL = "http://www.pdf995.com/samples/pdf.pdf";
try {
InputStream firstStream = new URL(externalFileURL).openStream();
TikaConfig tikaConfig = new TikaConfig();
MediaType mediaType = tikaConfig.getDetector().detect(TikaInputStream.get(firstStream), new Metadata());
firstStream.close();
InputStream secondStream = new URL(externalFileURL).openStream();
httpServletResponse.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", "DownloadMe." + mediaType.getSubtype()));
httpServletResponse.setContentType(mediaType.getBaseType().toString());
FileCopyUtils.copy(secondStream, httpServletResponse.getOutputStream());
httpServletResponse.flushBuffer();
} catch (Exception e) {
}
}
detect()
的 Javadoc 说:
The given stream is guaranteed to support the
mark feature
and the detector is expected tomark
the stream before reading any bytes from it, and toreset
the stream before returning.
TikaInputStream
的 Javadoc 说:
The created TikaInputStream instance keeps track of the original resource used to create it, while behaving otherwise just like a normal, buffered
InputStream
. A TikaInputStream instance is also guaranteed to support themark(int)
feature.
也就是说你应该使用TikaInputStream
来阅读内容,然后用try-with-resources来关闭它:
try (InputStream tikaStream = TikaInputStream.get(new URL(externalFileURL))) {
TikaConfig tikaConfig = new TikaConfig();
MediaType mediaType = tikaConfig.getDetector().detect(tikaStream, new Metadata());
httpServletResponse.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", "DownloadMe." + mediaType.getSubtype()));
httpServletResponse.setContentType(mediaType.getBaseType().toString());
FileCopyUtils.copy(tikaStream, httpServletResponse.getOutputStream());
httpServletResponse.flushBuffer();
}