web-dev-qa-db-ja.com

Spring-ローカルファイルシステムに保存せずに、データベースに大きなマルチパートファイルアップロードをストリーミングする方法

SpringブートのデフォルトのMultiPartResolverインターフェースは、ローカルファイルシステムに保存することにより、マルチパートファイルのアップロードを処理します。コントローラーメソッドを入力する前に、マルチパートファイル全体のサーバーへのアップロードを完了する必要があります。

アップロードされたすべてのファイルをデータベースに直接保存しており、サーバーのディスククォータは非常に小さいため、大きなファイルがアップロードされた場合、IOExeption - Disk quota exceeded

SpringのMultiPartResolverがローカルファイルシステムにファイルを保存してからdbに直接ストリーミングできるようにする前に、クライアントの着信リクエストから直接ストリームを取得する方法はありますか?

16
McLovin

ここで説明されているように、Apacheを直接使用できます https://commons.Apache.org/proper/commons-fileupload/streaming.html

@Controller
public class UploadController {

    @RequestMapping("/upload")
    public String upload(HttpServletRequest request) throws IOException, FileUploadException {

        ServletFileUpload upload = new ServletFileUpload();

        FileItemIterator iterator = upload.getItemIterator(request);
        while (iterator.hasNext()) {
            FileItemStream item = iterator.next();

            if (!item.isFormField()) {
                InputStream inputStream = item.openStream();
                //...
            }
        }
    }
}

スプリングのマルチパート解決メカニズムを必ず無効にしてください。

application.yml:

spring:
   http:
      multipart:
         enabled: false
16
Bernhard

実際、それは簡単な作業ではありません。クライアントからデータベースにストリームを直接書き込みたい場合は、リクエストを手動で処理する必要があります。このタスクをより簡単にするライブラリがいくつかあります。それらの1つは "Apache Commons FileUpload" です。以下の非常に単純な例では、着信multipart/form-dataこのライブラリによるリクエスト。

@Controller
public class Controller{

    @RequestMapping("/upload")
    public String upload(HttpServletRequest request){

        String boundary = extractBoundary(request);

        try {
            MultipartStream multipartStream = new MultipartStream(request.getInputStream(), 
                boundary.getBytes(), 1024, null);
            boolean nextPart = multipartStream.skipPreamble();
            while(nextPart) {
                String header = multipartStream.readHeaders();

                if(header.contains("filename")){
                    //if input is file
                    OutputStream output = createDbOutputStream();
                    multipartStream.readBodyData(output);
                    output.flush();
                    output.close();
                } else {
                    //if input is not file (text, checkbox etc)
                    ByteArrayOutputStream output = new ByteArrayOutputStream();
                    multipartStream.readBodyData(output);
                    String value = output.toString("utf-8");
                    //... do something with extracted value
                }
                nextPart = multipartStream.readBoundary();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }    
    }

    private String extractBoundary(HttpServletRequest request) {
        String boundaryHeader = "boundary=";
        int i = request.getContentType().indexOf(boundaryHeader)+
            boundaryHeader.length();
        return request.getContentType().substring(i);
    }    
}

ファイルフィールドのヘッダーは次のようになります。

Content-Disposition: form-data; name="fieldName"; filename="fileName.jpg"
Content-Type: image/jpeg

シンプルフィールドのヘッダーは次のようになります。

Content-Disposition: form-data; name="fieldName";

このスニペットは、方向を示すための簡単な例にすぎないことに注意してください。ヘッダーからフィールド名を抽出する、データベース出力ストリームを作成するなどの詳細はありません。これらすべてを独自に実装できます。 RFC1867 にあるマルチパートリクエストのフィールドヘッダーの例。についての情報 multipart/form-dataRFC2388

7
Ken Bekov