「ストリーミング」Apache Commons File Upload APIを使用して大きなファイルをアップロードしようとしています。
デフォルトのSpring MultipartアップローダーではなくApache Commons File Uploaderを使用しているのは、非常に大きなファイルサイズ(約2GB)をアップロードすると失敗するためです。このようなファイルのアップロードが一般的なGISアプリケーションで作業しています。
ファイルアップロードコントローラーの完全なコードは次のとおりです。
_@Controller
public class FileUploadController {
@RequestMapping(value="/upload", method=RequestMethod.POST)
public void upload(HttpServletRequest request) {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
// Inform user about invalid request
return;
}
//String filename = request.getParameter("name");
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
// Parse the request
try {
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (item.isFormField()) {
System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected.");
} else {
System.out.println("File field " + name + " with file name " + item.getName() + " detected.");
// Process the input stream
OutputStream out = new FileOutputStream("incoming.gz");
IOUtils.copy(stream, out);
stream.close();
out.close();
}
}
}catch (FileUploadException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
@RequestMapping(value = "/uploader", method = RequestMethod.GET)
public ModelAndView uploaderPage() {
ModelAndView model = new ModelAndView();
model.setViewName("uploader");
return model;
}
}
_
問題は、getItemIterator(request)
が常にアイテムを持たないイテレータを返すことです(つまり、iter.hasNext()
)は常にfalse
を返します。
私のapplication.propertiesファイルは次のとおりです。
_spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:19095/authdb
spring.datasource.username=georbis
spring.datasource.password=asdf123
logging.level.org.springframework.web=DEBUG
spring.jpa.hibernate.ddl-auto=update
multipart.maxFileSize: 128000MB
multipart.maxRequestSize: 128000MB
server.port=19091
_
_/uploader
_のJSPビューは次のとおりです。
_<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload">
File to upload: <input type="file" name="file"><br />
Name: <input type="text" name="name"><br /> <br />
Press here to upload the file!<input type="submit" value="Upload">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
_
何が間違っているのでしょうか?
M.Deinumの非常に役立つコメントのおかげで、私は問題を解決することができました。元の投稿の一部を整理し、今後の参考のために完全な回答として投稿しています。
私が最初に犯した間違いは、Springが提供するデフォルトのMultipartResolver
を無効にしなかったことです。これは、リゾルバーがHttpServeletRequest
を処理し、コントローラーが処理する前にそれを消費することになりました。
M. Deinumのおかげで、無効にする方法は次のとおりでした。
multipart.enabled=false
しかし、この後、私を待っている別の隠れた落とし穴がまだありました。デフォルトのマルチパートリゾルバを無効にするとすぐに、アップロードしようとしたときに次のエラーが発生し始めました。
Fri Sep 25 20:23:47 IST 2015
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
私のセキュリティ構成では、CSRF保護を有効にしていました。そのため、POSTリクエストを次の方法で送信する必要がありました。
<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload?${_csrf.parameterName}=${_csrf.token}">
<input type="file" name="file"><br>
<input type="submit" value="Upload">
</form>
</body>
</html>
また、コントローラーを少し変更しました。
@Controller
public class FileUploadController {
@RequestMapping(value="/upload", method=RequestMethod.POST)
public @ResponseBody Response<String> upload(HttpServletRequest request) {
try {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
// Inform user about invalid request
Response<String> responseObject = new Response<String>(false, "Not a multipart request.", "");
return responseObject;
}
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
// Parse the request
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (!item.isFormField()) {
String filename = item.getName();
// Process the input stream
OutputStream out = new FileOutputStream(filename);
IOUtils.copy(stream, out);
stream.close();
out.close();
}
}
} catch (FileUploadException e) {
return new Response<String>(false, "File upload error", e.toString());
} catch (IOException e) {
return new Response<String>(false, "Internal server IO error", e.toString());
}
return new Response<String>(true, "Success", "");
}
@RequestMapping(value = "/uploader", method = RequestMethod.GET)
public ModelAndView uploaderPage() {
ModelAndView model = new ModelAndView();
model.setViewName("uploader");
return model;
}
}
responseは、私が使用する単純な汎用応答タイプです。
public class Response<T> {
/** Boolean indicating if request succeeded **/
private boolean status;
/** Message indicating error if any **/
private String message;
/** Additional data that is part of this response **/
private T data;
public Response(boolean status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
// Setters and getters
...
}
最新バージョンのスプリングブートを使用している場合(2.0.0.M7を使用しています)、プロパティ名が変更されています。 Springはテクノロジー固有の名前の使用を開始しました
spring.servlet.multipart.maxFileSize = -1
spring.servlet.multipart.maxRequestSize = -1
spring.servlet.multipart.enabled = false
複数の実装がアクティブであるためにStreamClosed例外が発生する場合、最後のオプションを使用すると、デフォルトのスプリング実装を無効にできます
application.propertiesファイルにspring.http.multipart.enabled=false
を追加してみてください。
単純にスプリングプロパティを追加できます。
spring.servlet.multipart.max-file-size=20000KB
spring.servlet.multipart.max-request-size=20000KB
ここで、最大ファイルサイズは20000KBですが、必要に応じて変更できます。
Kindeditor + springbootを使用します。 (MultipartHttpServletRequest)要求を使用する場合。ファイルを取得できましたが、戻り値がnullのappeche-common-io:upload.parse(request)を使用します。
public BaseResult uploadImg(HttpServletRequest request,String type){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();