web-dev-qa-db-fra.com

Spring - Comment diffuser des fichiers volumineux en plusieurs parties vers la base de données sans les stocker sur le système de fichiers local

L'interface MultiPartResolver par défaut de Spring Boot gère le téléchargement de fichiers en plusieurs parties en les stockant sur le système de fichiers local. Avant la saisie de la méthode du contrôleur, l'intégralité du fichier en plusieurs parties doit terminer le téléchargement sur le serveur.

Nous stockons tous nos fichiers téléchargés directement dans une base de données et nos serveurs ont un très petit quota de disque, donc si un gros fichier est téléchargé, nous voyons un IOExeption - Disk quota exceeded.

Existe-t-il un moyen d'obtenir le flux directement à partir de la demande entrante du client avant que MultiPartResolver de Spring stocke le fichier sur le système de fichiers local afin que nous puissions diffuser directement sur notre base de données?

16
McLovin

Vous pouvez utiliser Apache directement, comme décrit ici 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();
                //...
            }
        }
    }
}

Assurez-vous de désactiver le mécanisme de résolution en plusieurs parties des ressorts.

application.yml:

spring:
   http:
      multipart:
         enabled: false
16
Bernhard

En fait, ce n'est pas une tâche triviale. Si vous souhaitez écrire le flux du client directement dans la base de données, vous devez traiter la demande manuellement. Il existe des bibliothèques qui peuvent simplifier cette tâche. L'un d'eux est "Apache Commons FileUpload" . Ci-dessous un exemple très simple, comment pouvez-vous traiter _ multipart/form-data demande de cette bibliothèque.

@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);
    }    
}

L'en-tête du champ de fichier ressemble à ceci:

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

L'en-tête d'un champ simple ressemble à ceci:

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

Notez que cet extrait n'est qu'un exemple simplifié pour vous indiquer la direction. Il n'y a pas de détails comme: extraire le nom du champ de l'en-tête, créer un flux de sortie de base de données, etc. Vous pouvez implémenter tout cela par vous-même. Vous pouvez trouver des exemples d'en-têtes de champs de demande en plusieurs parties dans RFC1867 . Des informations sur multipart/form-dataRFC2388 .

7
Ken Bekov