[java] 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)



답변

소개

업로드 할 파일을 찾아서 선택하려면 <input type="file">양식 의 HTML 필드 가 필요합니다 . 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. 기본 형식 enctype 만 지원합니다 application/x-www-form-urlencoded. request.getParameter()및 배우자들 모두 반환 null멀티 폼 데이터를 사용하는 경우. 여기에서 잘 알려진 Apache Commons FileUpload 가 등장했습니다.

수동으로 구문 분석하지 마십시오!

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

이미 Servlet 3.0 이상을 사용중인 경우 기본 API를 사용하십시오.

최소한 Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3 등)을 사용 HttpServletRequest#getPart()하는 경우 제공된 표준 API를 사용 하여 개별 멀티 파트 양식 데이터 항목을 수집 할 수 있습니다 (대부분의 Servlet 3.0 구현은 실제로 Apache를 사용합니다) 이것에 대한 커버 아래의 Commons FileUpload!). 또한 일반적인 양식 필드는 getParameter()일반적인 방법으로 사용할 수 있습니다 .

먼저 서블릿에 주석을 달아 요청을 @MultipartConfig인식하고 지원 하여 작동하게하십시오.multipart/form-datagetPart()

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

아직 Servlet 3.1이 아닌 경우 수동으로 제출 된 파일 이름을 얻습니다.

Part#getSubmittedFileName()서블릿 3.1 (톰캣 8 부두 9 제이보스 8 글래스 피쉬 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를 사용하십시오.

아직 Servlet 3.0을 사용하고 있지 않은 경우 (업그레이드 할 시점이 아닙니까?) 일반적인 방법은 Apache Commons FileUpload 를 사용 하여 여러 형식의 데이터 요청을 구문 분석하는 것입니다. 훌륭한 사용 설명서FAQ가 있습니다 (둘 다주의해서 살펴보십시오). O’Reilly ( ” cos “) MultipartRequest도 있지만 약간의 버그가 있으며 몇 년 동안 더 이상 활발하게 유지되지 않습니다. 나는 그것을 사용하지 않는 것이 좋습니다. Apache Commons FileUpload는 여전히 활발하게 유지되고 있으며 현재는 매우 성숙합니다.

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

커먼즈 IO를 잊었 기 때문에 초기 시도가 실패했을 가능성이 높습니다.

여기에 어떻게 킥오프 예입니다 doPost()당신의이 UploadServlet아파치 코 몬즈는 FileUpload를 사용할 때처럼 보일 수는 :

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)도 빈 목록을 반환합니다 .

를 참고하십시오 FilenameUtils#getName(). 파일 이름을 얻는 방법에 대한 MSIE 수정 프로그램입니다. 이 브라우저는 파일 이름 대신 이름을 따라 전체 파일 경로를 잘못 보냅니다.

또는이 모든 Filter것을 자동으로 구문 분석 request.getParameter()하고 일반적인 방법으로 계속 사용 하여 업로드 된 파일을 검색 할 수 있도록 요청을 매개 변수 맵에 다시 넣을 수도 있습니다 request.getAttribute(). 이 블로그 기사에서 예제를 찾을 수 있습니다 .

getParameter()여전히 반환 되는 GlassFish3 버그에 대한 해결 방법null

3.1.2 이전의 Glassfish 버전 은 여전히을 반환 하는 버그 가있었습니다 . 이러한 컨테이너를 대상으로하고 업그레이드 할 수없는 경우이 유틸리티 방법 을 사용하여 값을 추출해야합니다 .getParameter()nullgetPart()

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">

업로드 된 파일을 저장하면 (사용하지 getRealPath()않고 part.write()!)

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

업로드 된 파일 제공

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

양식 아약시

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


이 모든 것이 도움이되기를 바랍니다 🙂


답변

Spring MVC를 사용하는 경우 다음과 같은 방법입니다. (누군가 유용하다고 생각하면 여기에 남겨 둡니다).

enctype속성이 ” multipart/form-data“(BalusC의 답변과 동일)로 설정된 양식을 사용하십시오.

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

컨트롤러에서 요청 매개 변수 fileMultipartFile다음과 같이 유형에 맵핑하십시오 .

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public void handleUpload(@RequestParam("file") MultipartFile file) throws IOException {
    if (!file.isEmpty()) {
            byte[] bytes = file.getBytes(); // alternatively, file.getInputStream();
            // application logic
    }
}

다음을 사용하여 파일 이름과 크기를 얻을 수 MultipartFilegetOriginalFilename()getSize().

나는 이것을 스프링 버전으로 테스트했다 4.1.1.RELEASE.


답변

당신은 필요 common-io.1.4.jar당신에 포함 할 파일을 lib디렉토리, 또는 어떤 편집기에서 작업하는 경우, 넷빈즈처럼, 당신은 프로젝트 속성에 가서 바로 JAR 파일을 추가 할 필요가 당신이 수행됩니다.

얻으려면 common.io.jar파일을 그냥 구글하거나 아파치로 이동 톰캣 이 파일의 무료 다운로드 할 수있는 옵션을 얻을 경우 웹 사이트. 그러나 Windows 사용자 인 경우 바이너리 ZIP 파일을 다운로드하십시오.


답변

Tomcat 6 o 7에 컴포넌트 또는 외부 라이브러리가없는 경우

web.xml 파일 에서 업로드 활성화

http://joseluisbz.wordpress.com/2014/01/17/manually-installing-php-tomcat-and-httpd-lounge/#Enabling%20File%20Uploads .

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <multipart-config>
      <max-file-size>3145728</max-file-size>
      <max-request-size>5242880</max-request-size>
    </multipart-config>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

당신이 볼 수 있듯이 :

    <multipart-config>
      <max-file-size>3145728</max-file-size>
      <max-request-size>5242880</max-request-size>
    </multipart-config>

JSP를 사용하여 파일 업로드 파일 :

html 파일에서

<form method="post" enctype="multipart/form-data" name="Form" >

  <input type="file" name="fFoto" id="fFoto" value="" /></td>
  <input type="file" name="fResumen" id="fResumen" value=""/>

JSP 파일 또는 서블릿에서

    InputStream isFoto = request.getPart("fFoto").getInputStream();
    InputStream isResu = request.getPart("fResumen").getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte buf[] = new byte[8192];
    int qt = 0;
    while ((qt = isResu.read(buf)) != -1) {
      baos.write(buf, 0, qt);
    }
    String sResumen = baos.toString();

max-file-size , max-request-size 및 설정할 수있는 기타 옵션과 같은 서블릿 요구 사항에 맞게 코드를 편집하십시오 .


답변

첨부 파일이 있는지 여부에 관계없이 모든 HTML 양식에 공통 서블릿을 사용 하고 있습니다. 이 서블릿은 TreeMap키가 jsp 이름 인 위치를 리턴합니다 . 매개 변수 및 값은 사용자 입력이며 모든 첨부 파일을 고정 된 디렉토리에 저장하고 나중에 선택한 디렉토리의 이름을 변경합니다. Connections는 연결 오브젝트가있는 사용자 정의 인터페이스입니다. 나는 이것이 당신을 도울 것이라고 생각합니다

public class ServletCommonfunctions extends HttpServlet implements
        Connections {

    private static final long serialVersionUID = 1L;

    public ServletCommonfunctions() {}

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {}

    public SortedMap<String, String> savefilesindirectory(
            HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        // Map<String, String> key_values = Collections.synchronizedMap( new
        // TreeMap<String, String>());
        SortedMap<String, String> key_values = new TreeMap<String, String>();
        String dist = null, fact = null;
        PrintWriter out = response.getWriter();
        File file;
        String filePath = "E:\\FSPATH1\\2KL06CS048\\";
        System.out.println("Directory Created   ????????????"
            + new File(filePath).mkdir());
        int maxFileSize = 5000 * 1024;
        int maxMemSize = 5000 * 1024;
        // Verify the content type
        String contentType = request.getContentType();
        if ((contentType.indexOf("multipart/form-data") >= 0)) {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // maximum size that will be stored in memory
            factory.setSizeThreshold(maxMemSize);
            // Location to save data that is larger than maxMemSize.
            factory.setRepository(new File(filePath));
            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload(factory);
            // maximum file size to be uploaded.
            upload.setSizeMax(maxFileSize);
            try {
                // Parse the request to get file items.
                @SuppressWarnings("unchecked")
                List<FileItem> fileItems = upload.parseRequest(request);
                // Process the uploaded file items
                Iterator<FileItem> i = fileItems.iterator();
                while (i.hasNext()) {
                    FileItem fi = (FileItem) i.next();
                    if (!fi.isFormField()) {
                        // Get the uploaded file parameters
                        String fileName = fi.getName();
                        // Write the file
                        if (fileName.lastIndexOf("\\") >= 0) {
                            file = new File(filePath
                                + fileName.substring(fileName
                                        .lastIndexOf("\\")));
                        } else {
                            file = new File(filePath
                                + fileName.substring(fileName
                                        .lastIndexOf("\\") + 1));
                        }
                        fi.write(file);
                    } else {
                        key_values.put(fi.getFieldName(), fi.getString());
                    }
                }
            } catch (Exception ex) {
                System.out.println(ex);
            }
        }
        return key_values;
    }
}


답변

Spring MVC의
경우이 작업을 몇 시간 동안 시도했지만 데이터와 이미지를 모두 입력하는 데 도움이되는 간단한 버전을 관리했습니다.

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

처리 할 컨트롤러

@Controller
public class FormController {
    @RequestMapping(value="/handleform",method= RequestMethod.POST)
    ModelAndView register(@RequestParam String name, @RequestParam int age, @RequestParam MultipartFile file)
            throws ServletException, IOException {

        System.out.println(name);
        System.out.println(age);
        if(!file.isEmpty()){
            byte[] bytes = file.getBytes();
            String filename = file.getOriginalFilename();
            BufferedOutputStream stream =new BufferedOutputStream(new FileOutputStream(new File("D:/" + filename)));
            stream.write(bytes);
            stream.flush();
            stream.close();
        }
        return new ModelAndView("index");
    }
}

그것이 도움이되기를 바랍니다 🙂


답변

이 문제의 또 다른 원인은 내장 Tomcat과 함께 Geronimo를 사용하는 경우 발생합니다. 이 경우 commons-io 및 commons-fileupload 테스트를 여러 번 반복 한 후 commons-xxx jar를 처리하는 상위 클래스 로더에서 문제가 발생합니다. 이를 방지해야합니다. 충돌은 항상 다음에서 발생했습니다.

fileItems = uploader.parseRequest(request);

fileItems의 List 유형은 commons-fileupload의 현재 버전으로 변경되어 List<FileItem>generic이었던 이전 버전과 달리 구체적 으로 변경되었습니다 List.

commons-fileupload 및 commons-io의 소스 코드를 Eclipse 프로젝트에 추가하여 실제 오류를 추적하고 마침내 통찰력을 얻었습니다. 먼저, 예외는 명시된 FileIOException이 아니고 예외가 아닌 Throwable 유형입니다 (이것은 트랩되지 않습니다). 둘째, 오류 메시지는 axis2가 commons-io를 찾을 수 없기 때문에 클래스를 찾을 수 없다고 언급했기 때문에 난독 화됩니다. Axis2는 내 프로젝트에서 전혀 사용되지 않지만 표준 설치의 일부로 Geronimo 리포지토리 하위 디렉토리에 폴더로 존재합니다.

마지막으로, 문제를 성공적으로 해결하는 효과적인 솔루션을 제공하는 1 곳을 발견했습니다. 배치 계획에서 상위 로더에서 jar을 숨겨야합니다. 이것은 아래 표시된 전체 파일로 geronimo-web.xml에 저장되었습니다.

Pasted from <http://osdir.com/ml/user-geronimo-apache/2011-03/msg00026.html> 



<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<web:web-app xmlns:app="http://geronimo.apache.org/xml/ns/j2ee/application-2.0" xmlns:client="http://geronimo.apache.org/xml/ns/j2ee/application-client-2.0" xmlns:conn="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2" xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2" xmlns:ejb="http://openejb.apache.org/xml/ns/openejb-jar-2.2" xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-2.0" xmlns:name="http://geronimo.apache.org/xml/ns/naming-1.2" xmlns:pers="http://java.sun.com/xml/ns/persistence" xmlns:pkgen="http://openejb.apache.org/xml/ns/pkgen-2.1" xmlns:sec="http://geronimo.apache.org/xml/ns/security-2.0" xmlns:web="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1">
    <dep:environment>
        <dep:moduleId>
            <dep:groupId>DataStar</dep:groupId>
            <dep:artifactId>DataStar</dep:artifactId>
            <dep:version>1.0</dep:version>
            <dep:type>car</dep:type>
        </dep:moduleId>

<!--Don't load commons-io or fileupload from parent classloaders-->
        <dep:hidden-classes>
            <dep:filter>org.apache.commons.io</dep:filter>
            <dep:filter>org.apache.commons.fileupload</dep:filter>
        </dep:hidden-classes>
        <dep:inverse-classloading/>


    </dep:environment>
    <web:context-root>/DataStar</web:context-root>
</web:web-app>