web-dev-qa-db-ja.com

Spring Boot:Apache Commons FileUploadを使用した大規模ストリーミングファイルのアップロード

「ストリーミング」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>
_

何が間違っているのでしょうか?

22
balajeerc

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
    ...
}
28
balajeerc

最新バージョンのスプリングブートを使用している場合(2.0.0.M7を使用しています)、プロパティ名が変更されています。 Springはテクノロジー固有の名前の使用を開始しました

spring.servlet.multipart.maxFileSize = -1

spring.servlet.multipart.maxRequestSize = -1

spring.servlet.multipart.enabled = false

複数の実装がアクティブであるためにStreamClosed例外が発生する場合、最後のオプションを使用すると、デフォルトのスプリング実装を無効にできます

10
ThetaSinner

application.propertiesファイルにspring.http.multipart.enabled=falseを追加してみてください。

1
HopeBing

単純にスプリングプロパティを追加できます。

spring.servlet.multipart.max-file-size=20000KB
spring.servlet.multipart.max-request-size=20000KB

ここで、最大ファイルサイズは20000KBですが、必要に応じて変更できます。

0
pranav patil

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();
0
user9814979