프로그래밍 언어/JSP

JSP / Servlet을 사용하여 서버에 파일을 업로드하는 방법

Rateye 2021. 6. 27. 14:30
728x90
반응형

 

질문 : JSP / Servlet을 사용하여 서버에 파일을 업로드하는 방법은 무엇입니까?

JSP / Servlet을 사용하여 서버에 파일을 업로드하려면 어떻게해야합니까? 나는 이것을 시도했다 :

<form action="upload" method="post">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

그러나 파일 내용이 아닌 파일 이름 만 얻습니다. enctype="multipart/form-data"<form> 에 추가하면 request.getParameter() null 반환합니다.

연구 중에 Apache Common FileUpload를 발견했습니다. 나는 이것을 시도했다 :

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); // This line is where it died.
            

불행히도 서블릿은 명확한 메시지와 원인없이 예외를 던졌습니다. 다음은 스택 추적입니다.

SEVERE: Servlet.service() for servlet UploadServlet threw exception
        javax.servlet.ServletException: Servlet execution threw an exception
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
        at java.lang.Thread.run(Thread.java:637)
                                                
답변

업로드 할 파일을 찾아보고 선택하려면 양식에 HTML <input type="file"> 필드가 필요합니다. HTML 사양에 명시된대로 POST 메서드를 사용해야 enctype "multipart/form-data" 로 설정해야합니다.

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

이러한 양식을 제출 한 후 이진 다중 파트 양식 데이터는 enctype 이 설정되지 않은 경우와 다른 형식으로 요청 본문에서 사용할 수 있습니다.

Servlet 3.0 이전에는 Servlet API가 기본적으로 multipart/form-data 지원하지 않았습니다. application/x-www-form-urlencoded 의 기본 양식 enctype 만 지원합니다. request.getParameter() 및 consorts는 다중 파트 양식 데이터를 사용할 때 null 여기에서 잘 알려진 Apache Commons FileUpload 가 등장했습니다.

ServletRequest#getInputStream() 기반으로 요청 본문을 직접 구문 분석 할 수 있습니다. 그러나 이것은 RFC2388에 대한 정확한 지식이 필요한 정확하고 지루한 작업입니다. 이 작업을 직접 시도하거나 인터넷의 다른 곳에서 찾을 수있는 자체 개발 한 라이브러리없는 코드를 복사하여 붙여 넣어서는 안됩니다. roseindia.net과 같은 많은 온라인 소스가 이것에 대해 열심히 실패했습니다. pdf 파일 업로드를 참조하십시오. 수백만 명의 사용자가 사용하는 (암시 적으로 테스트 된!) 실제 라이브러리를 사용해야합니다. 이러한 라이브러리는 견고 함을 입증했습니다.

Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3 등) 이상을 사용하는 경우 표준 API 제공 HttpServletRequest#getPart() 를 사용하여 개별 멀티 파트 양식 데이터 항목 (대부분의 서블릿 3.0 구현은 실제로이를 위해 Apache Commons FileUpload를 사용합니다!). 또한 일반 양식 필드는 일반적인 방법으로 getParameter()

먼저 서블릿에 @MultipartConfig multipart/form-data 요청을 인식하고 지원 getPart() 가 작동하도록합니다.

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
      

그런 다음 다음과 같이 doPost()

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String description = request.getParameter("description"); // Retrieves <input type="text" name="description">
Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
InputStream fileContent = filePart.getInputStream();
// ... (do your job here)
}

Path#getFileName() . 이것은 파일 이름을 얻기위한 MSIE 수정입니다. 이 브라우저는 파일 이름 만이 아닌 이름과 함께 전체 파일 경로를 잘못 보냅니다.

<input type="file" name="file" multiple="true" /> 경우 아래와 같이 수집합니다 (불행히도 request.getParts("file") ) :

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // ...
    List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName()) && part.getSize() > 0).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">
    
    for (Part filePart : fileParts) {
        String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
        InputStream fileContent = filePart.getInputStream();
        // ... (do your job here)
    }
}

Part#getSubmittedFileName() 은 Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4 등)에 도입되었습니다. 아직 Servlet 3.1을 사용하고 있지 않다면 제출 된 파일 이름을 얻으려면 추가 유틸리티 메서드가 필요합니다.

private static String getSubmittedFileName(Part part) {
    for (String cd : part.getHeader("content-disposition").split(";")) {
        if (cd.trim().startsWith("filename")) {
            String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
            return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
        }
    }
    return null;
}
String fileName = getSubmittedFileName(filePart);

파일 이름 가져 오기에 관한 MSIE 수정 사항을 참고하십시오. 이 브라우저는 파일 이름 만이 아닌 이름과 함께 전체 파일 경로를 잘못 보냅니다.

아직 Servlet 3.0을 사용하지 않는 경우 (업그레이드 할시기가 아닙니까?) 일반적인 관행은 Apache Commons FileUpload 를 사용하여 여러 부분 양식 데이터 요청을 구문 분석하는 것입니다. 그것은 훌륭한 사용자 가이드FAQ를 가지고 있습니다 (둘 다주의 깊게 살펴보십시오). O'Reilly ( " cos ") MultipartRequest 도 있지만 일부 (사소한) 버그가 있으며 수년 동안 더 이상 유지 관리되지 않습니다. 나는 그것을 사용하지 않는 것이 좋습니다. Apache Commons FileUpload는 여전히 활발히 유지되고 있으며 현재 매우 성숙합니다.

Apache Commons FileUpload를 사용하려면 웹앱의 /WEB-INF/lib 에 최소한 다음 파일이 있어야합니다.

커먼즈 IO를 잊어 버렸기 때문에 초기 시도가 실패했을 가능성이 큽니다.

다음은 Apache Commons FileUpload를 사용할 때 UploadServlet doPost() 가 어떻게 보이는지 킥오프 예제입니다.

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
        for (FileItem item : items) {
            if (item.isFormField()) {
                // Process regular form field (input type="text|radio|checkbox|etc", select, etc).
                String fieldName = item.getFieldName();
                String fieldValue = item.getString();
                // ... (do your job here)
            } else {
                // Process form file field (input type="file").
                String fieldName = item.getFieldName();
                String fileName = FilenameUtils.getName(item.getName());
                InputStream fileContent = item.getInputStream();
                // ... (do your job here)
            }
        }   
    } catch (FileUploadException e) {
        throw new ServletException("Cannot parse multipart request.", e);
    }

// ...
}

동일한 요청에 대해 미리 getParameter() , getParameterMap() , getParameterValues() , getInputStream() , getReader() 등을 호출하지 않는 것이 매우 중요합니다. 그렇지 않으면 서블릿 컨테이너가 요청 본문을 읽고 구문 분석하므로 Apache Commons FileUpload는 빈 요청 본문을 가져옵니다. 참조 ao ServletFileUpload # parseRequest (request) returns an empty list .

FilenameUtils#getName() 유의하십시오. 이것은 파일 이름을 얻기위한 MSIE 수정입니다. 이 브라우저는 파일 이름 만이 아닌 이름과 함께 전체 파일 경로를 잘못 보냅니다.

또는 당신은 또한이 모든 포장 할 수 Filter 모두 자동적으로 그것을 구문 분석하고 계속 사용 할 수 있도록 요청 된 parameterMap의 물건 등을 넣어 request.getParameter() 일반적인 방법과에 의해 업로드 된 파일 검색 request.getAttribute() . 이 블로그 기사에서 예제를 찾을 수 있습니다 .

3.1.2 이전의 Glassfish 버전에는 getParameter() 여전히 null 반환하는 버그가 있습니다. 이러한 컨테이너를 대상으로하고 업그레이드 할 수없는 경우 다음 유틸리티 메서드 getPart()

private static String getValue(Part part) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8"));
    StringBuilder value = new StringBuilder();
    char[] buffer = new char[1024];
    for (int length = 0; (length = reader.read(buffer)) > 0;) {
        value.append(buffer, 0, length);
    }
    return value.toString();
}
String description = getValue(request.getPart("description")); 
// Retrieves <input type="text" name="description">
                                                                                                                                                                                                                                                                                                                                                                                                                                                                

InputStream ( fileContent 변수)을 디스크 또는 데이터베이스에 올바르게 저장하는 방법에 대한 자세한 내용은 다음 답변으로 이동하십시오.

디스크 또는 데이터베이스에서 클라이언트로 다시 저장된 파일을 올바르게 제공하는 방법에 대한 자세한 내용은 다음 답변을 참조하십시오.

Ajax (및 jQuery)를 사용하여 업로드하는 방법은 다음 답변으로 이동하십시오. 이를 위해 양식 데이터를 수집하는 서블릿 코드를 변경할 필요가 없습니다. 응답하는 방식 만 변경 될 수 있지만 이것은 다소 사소한 것입니다 (예 : JSP로 전달하는 대신 Ajax 호출을 담당하는 스크립트가 기대하는 것에 따라 JSON 또는 XML 또는 일반 텍스트를 인쇄하십시오).

이 모든 것이 도움이되기를 바랍니다. :)

출처 : https://stackoverflow.com/questions/2422468/how-to-upload-files-to-server-using-jsp-servlet
728x90
반응형

'프로그래밍 언어 > JSP' 카테고리의 다른 글

JSF, Servlet 및 JSP의 차이점  (0) 2021.06.27