서블릿 애플리케이션에 업로드 된 파일을 저장하는 권장 방법


121

나는 여기 에서 파일이 이식 가능하지 않고 트랜잭션적이고 외부 매개 변수가 필요하기 때문에 어쨌든 서버에 파일을 저장해서는 안된다는 것을 읽었습니다 . 그러나 tomcat (7)에 대한 tmp 솔루션이 필요하고 알고 싶은 서버 시스템에 대한 (상대적) 제어권이 있다는 점을 감안할 때 :

  • 파일을 저장하기에 가장 좋은 장소는 어디입니까? /WEB-INF/uploads( 여기 에 대해 권고 됨 ) 또는 아래 $CATALINA_BASE( 여기 참조 ) 또는 ...에 저장해야합니까 ? JavaEE 6 자습서 는 사용자 (: wtf :) 로부터 경로를 가져옵니다 . 주의 : 파일을 어떤 방법으로도 다운로드 할 수 없어야합니다.

  • 여기에 설명 된대로 구성 매개 변수를 설정해야합니까 ? 나는 일부 코드를 고맙게 생각합니다 (상대 경로를 제공하고 싶습니다-적어도 Tomcat 이식 가능)- Part.write()유망 해 보이지만 분명히 절대 경로가 필요합니다.

  • 이 접근법의 단점과 데이터베이스 / JCR 저장소의 단점에 대한 설명에 관심이 있습니다.

불행히도 @BalusC 의 FileServlet 은 파일 다운로드에 집중하고 파일 업로드에 대한 그의 대답 은 파일 저장 위치에 대한 부분을 건너 뜁니다.

DB 또는 JCR 구현 (예 : jackrabbit ) 을 사용하도록 쉽게 변환 할 수있는 솔루션 이 바람직합니다.


내 마지막 방법은 아래 답변
Mr_and_Mrs_D

답변:


165

페이지를 새로 고친 후에 만 ​​사용할 수있는 업로드 된 이미지 에 대한 답변에 언급 된 이유로 IDE의 프로젝트 폴더 (일명 서버의 배포 폴더)를 제외하고 액세스 가능한 위치에 저장합니다 .

  1. IDE 프로젝트 폴더의 변경 사항은 서버의 작업 폴더에 즉시 반영되지 않습니다. IDE에는 서버의 작업 폴더가 마지막 업데이트와 동기화되도록하는 일종의 백그라운드 작업이 있습니다 ( "게시"라고하는 IDE 용어). 이것이 당신이보고있는 문제의 주요 원인입니다.

  2. 실제 코드에서는 업로드 된 파일을 webapp의 배포 폴더에 저장하는 것이 전혀 작동하지 않는 상황이 있습니다. 일부 서버는 기본적으로 또는 구성에 따라 배포 된 WAR 파일을 로컬 디스크 파일 시스템으로 확장하지 않고 대신 메모리에서 완전히 확장합니다. 기본적으로 배포 된 WAR 파일을 편집하고 다시 배포하지 않고는 메모리에 새 파일을 만들 수 없습니다.

  3. 서버가 배포 된 WAR 파일을 로컬 디스크 파일 시스템으로 확장하더라도 새 파일이 원래 WAR 파일의 일부가 아니기 때문에 새로 생성 된 모든 파일은 재배포 또는 간단한 재시작시 손실됩니다.

method를 사용 하지 않는getRealPath() 나 또는 다른 사람에게 정확히 로컬 디스크 파일 시스템에 저장되는 것은 중요 하지 않습니다 . 그 방법을 사용하는 것은 어쨌든 놀라운 일입니다.

저장 위치에 대한 경로는 여러 방법으로 정의 될 수 있습니다. 모든 것을 혼자서 해야합니다 . 아마도 이것은 서버가 모든 것을 자동으로 수행 할 것으로 예상했기 때문에 혼란이 발생하는 곳일 것입니다. 주의하시기 바랍니다 @MultipartConfig(location)않습니다 하지 최종 업로드 대상을 지정하지만, 사건 파일 크기에 대한 임시 저장 위치는 메모리 저장 임계 값을 초과합니다.

따라서 최종 저장 위치에 대한 경로는 다음 방법 중 하나로 정의 할 수 있습니다.

  • 하드 코딩 됨 :

      File uploads = new File("/path/to/uploads");
  • 다음을 통한 환경 변수 SET UPLOAD_LOCATION=/path/to/uploads:

      File uploads = new File(System.getenv("UPLOAD_LOCATION"));
  • 다음을 통해 서버 시작 중 VM 인수 -Dupload.location="/path/to/uploads":

      File uploads = new File(System.getProperty("upload.location"));
  • *.properties파일 항목 upload.location=/path/to/uploads:

      File uploads = new File(properties.getProperty("upload.location"));
  • web.xml <context-param>이름 upload.location과 가치 /path/to/uploads:

      File uploads = new File(getServletContext().getInitParameter("upload.location"));
  • 있는 경우 서버 제공 위치를 사용합니다 (예 : JBoss AS / WildFly) .

      File uploads = new File(System.getProperty("jboss.server.data.dir"), "uploads");

어느 쪽이든 다음과 같이 파일을 쉽게 참조하고 저장할 수 있습니다.

File file = new File(uploads, "somefilename.ext");

try (InputStream input = part.getInputStream()) {
    Files.copy(input, file.toPath());
}

또는 사용자가 우연히 동일한 이름으로 기존 파일을 덮어 쓰지 못하도록 고유 한 파일 이름을 자동 생성하려는 경우 :

File file = File.createTempFile("somefilename-", ".ext", uploads);

try (InputStream input = part.getInputStream()) {
    Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

partJSP / Servlet에서 얻는 방법 은 JSP / Servlet 을 사용하여 서버에 파일을 업로드하는 방법에 나와 있습니다. partJSF 에서 얻는 방법어떻게 JSF 2.2 <h : inputFile>을 사용하여 파일을 업로드합니까? 저장된 파일은 어디에 있습니까?

참고 :에 정의 된 임시 저장 위치에 상대적인 경로를 해석하므로 사용 하지 마십시오 .Part#write()@MultipartConfig(location)

또한보십시오:


@MultipartConfig(location)지정 임시 파일 크기가 메모리 저장에 대한 임계 값을 초과 할 때 서버에서 사용 storge 위치는, 당신이 궁극적으로 원하는하지 영구 저장 위치에 저장합니다. 이 값은 java.io.tmpdir시스템 속성 으로 식별되는 경로로 기본 설정됩니다 . 실패한 JSF 시도에 대한이 관련 답변도 참조하십시오 : stackoverflow.com/questions/18478154/…
BalusC

1
감사합니다-내가 바보처럼 들리지 않기를 바라지 만 Part.write>> 이 인용문을 사용하면 특정 구현에서 가능한 경우 모든 기본 데이터를 복사하는 대신 파일 이름 변경을 사용할 수 있으므로 일부 데이터 와 함께 상당한 성능 이점을 얻을 수 있습니다. 일부 아파치 라이브러리에서 알 수없는 "잘라 내기"(vs 복사) 방법을 사용하면 바이트를 직접 작성하고 이미 파일을 다시 만드는 번거 로움을 덜 수 있습니다 ( 여기도 참조 )
Mr_and_Mrs_D

예, 이미 Servlet 3.0을 사용하고 있다면 Part#write(). 나는 그것으로 대답을 업데이트했습니다.
BalusC 2013 년

게시물을 업데이트 해주셔서 감사합니다. Tomcat에 다음과 같은 속성이 "jboss.server.data.dir"있습니까?
Mr_and_Mrs_D

1
아니요, 없습니다.
BalusC 2013 년

7

받아 들인 답변을 기반으로 최종 방법을 게시합니다.

@SuppressWarnings("serial")
@WebServlet("/")
@MultipartConfig
public final class DataCollectionServlet extends Controller {

    private static final String UPLOAD_LOCATION_PROPERTY_KEY="upload.location";
    private String uploadsDirName;

    @Override
    public void init() throws ServletException {
        super.init();
        uploadsDirName = property(UPLOAD_LOCATION_PROPERTY_KEY);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // ...
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        Collection<Part> parts = req.getParts();
        for (Part part : parts) {
            File save = new File(uploadsDirName, getFilename(part) + "_"
                + System.currentTimeMillis());
            final String absolutePath = save.getAbsolutePath();
            log.debug(absolutePath);
            part.write(absolutePath);
            sc.getRequestDispatcher(DATA_COLLECTION_JSP).forward(req, resp);
        }
    }

    // helpers
    private static String getFilename(Part part) {
        // courtesy of BalusC : http://stackoverflow.com/a/2424824/281545
        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;
    }
}

어디 :

@SuppressWarnings("serial")
class Controller extends HttpServlet {

    static final String DATA_COLLECTION_JSP="/WEB-INF/jsp/data_collection.jsp";
    static ServletContext sc;
    Logger log;
    // private
    // "/WEB-INF/app.properties" also works...
    private static final String PROPERTIES_PATH = "WEB-INF/app.properties";
    private Properties properties;

    @Override
    public void init() throws ServletException {
        super.init();
        // synchronize !
        if (sc == null) sc = getServletContext();
        log = LoggerFactory.getLogger(this.getClass());
        try {
            loadProperties();
        } catch (IOException e) {
            throw new RuntimeException("Can't load properties file", e);
        }
    }

    private void loadProperties() throws IOException {
        try(InputStream is= sc.getResourceAsStream(PROPERTIES_PATH)) {
                if (is == null)
                    throw new RuntimeException("Can't locate properties file");
                properties = new Properties();
                properties.load(is);
        }
    }

    String property(final String key) {
        return properties.getProperty(key);
    }
}

및 /WEB-INF/app.properties :

upload.location=C:/_/

HTH 및 버그를 찾으면 알려주십시오.


1
두 가지 (win / ux) 경우 모두에서 작동하는 SO 독립 솔루션을 원한다면 어떻게합니까? 다른 upload.location 경로를 설정해야합니까? 아니면 다른 힌트가 있습니까?
pikimota
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.