IT

Spring Boot: Apache Commons File Upload를 사용한 대용량 스트리밍 파일 업로드

itgroup 2023. 3. 4. 14:44
반응형

Spring Boot: Apache Commons File Upload를 사용한 대용량 스트리밍 파일 업로드

'스트리밍' Apache Commons File Upload API를 사용하여 대용량 파일을 업로드하려고 합니다.

Apache Commons File Uploader를 사용하는 이유는 기본 Spring Multipart 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

의 JSP 뷰/uploader는 다음과 같습니다.

<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의 매우 도움이 되는 코멘트 덕분입니다.디넘, 내가 문제를 해결했어저는 저의 원래 게시물을 정리했고, 나중에 참조할 수 있도록 완전한 답변으로 이 글을 올립니다.

첫 번째 실수는 디폴트를 무효로 하지 않은 것입니다.MultipartResolver그 봄이 제공하는 것.이는 결국 리졸바(Resolver)가 처리하게 되었습니다.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.servlet.multipart.maxFileSize=-1

spring.servlet.multipart.maxRequestSize=-1

spring.servlet.servart.enabled=false

여러 구현이 활성화되어 Stream Closed 예외가 발생하는 경우 마지막 옵션을 사용하면 기본 스프링 구현을 비활성화할 수 있습니다.

추가해 보세요spring.http.multipart.enabled=falseapplication.properties 파일에 저장됩니다.

킨디터+스프링부츠를 사용합니다.(MultipartHttpServletRequest) 요구를 사용하는 경우.파일을 가져올 수 있었지만 apeche-common-io:upload.parse(request)를 사용하여 반환값이 null입니다.

public BaseResult uploadImg(HttpServletRequest request,String type){
                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
                MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();

스프링 속성을 추가할 수 있습니다.

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

여기서 나의 최대 파일 사이즈는 20000KB입니다.필요에 따라서 변경할 수 있습니다.

언급URL : https://stackoverflow.com/questions/32782026/springboot-large-streaming-file-upload-using-apache-commons-fileupload

반응형