tomcat - 將上傳的文件保存在servlet應用程序中的推薦方法





servlets file-upload servlet-3.0 (3)


我根據公認的答案發布我的最終做法:

@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,如果你發現一個錯誤讓我知道

我here讀到,不應該將文件保存在服務器中,因為它不是可移植的,事務性的並且需要外部參數。 然而,鑑於我需要一個tomcat(7)的tmp解決方案,並且我有(相對)我想知道的服務器機器的控制權:

  • 什麼是保存文件的最佳地點? 我應該將它保存在/WEB-INF/uploads (在here建議)還是在$CATALINA_BASE (見here )或...下的某個地方? JavaEE 6教程獲取用戶的路徑 (:wtf :)。 注意:文件不得以任何方式下載。

  • 我應該如何設置配置參數? 我會欣賞一些代碼(我寧願給它一個相對路徑 - 所以它至少是Tomcat的便攜式) - Part.write()看起來很有前途 - 但顯然需要一個絕對路徑

  • 我想對這種方法與數據庫/ JCR存儲庫的缺點進行闡述

不幸的是@BalusC的FileServlet專注於下載文件,而他關於上傳文件的answer跳過了保存文件的部分。

可以輕鬆轉換為使用DB或JCR實現(如jackrabbit )的解決方案將是更可取的。




將其存儲在IDE的項目文件夾(即服務器的部署文件夾除外)的可訪問位置的任何位置,原因僅限於刷新頁面後可用的載映像的答案中提到的原因:

  1. IDE的項目文件夾中的更改不會立即反映在服務器的工作文件夾中。 IDE中有一種後台作業,它保證服務器的工作文件夾與上次更新同步(這在IDE中稱為“發布”)。 這是你看到問題的主要原因。

  2. 在真實世界的代碼中,存在將上傳的文件存儲在webapp的deploy文件夾中根本不起作用的情況。 一些服務器(默認或通過配置)不會將已部署的WAR文件擴展到本地磁盤文件系統,而是完全在內存中。 如果沒有基本編輯已部署的WAR文件並重新部署它,則無法在內存中創建新文件。

  3. 即使服務器將部署的WAR文件擴展到本地磁盤文件系統中,所有新創建的文件在重新部署或甚至簡單重新啟動時都會丟失,因為這些新文件不是原始WAR文件的一部分。

只要你沒有使用過getRealPath()方法,對於我或者其他任何在本地磁盤文件系統中將被保存的人來說沒有 getRealPath() 。 無論如何,使用這種方法都是令人擔憂的。

存儲位置的路徑可以通過多種方式進行定義。 你必須自己做。 也許這是你的困惑造成的地方,因為你以某種方式期望服務器全部自動完成。 請注意, @MultipartConfig(location)不指定最終的上載目標,但案例文件大小的臨時存儲位置超過了內存存儲閾值。

所以,最終存儲位置的路徑可以通過以下任一方式來定義:

  • 硬編碼:

    File uploads = new File("/path/to/uploads");
    
  • 通過SET UPLOAD_LOCATION=/path/to/uploads環境變量SET UPLOAD_LOCATION=/path/to/uploads

    File uploads = new File(System.getenv("UPLOAD_LOCATION"));
    
  • 通過-Dupload.location="/path/to/uploads"啟動服務器時的VM參數:

    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和value /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);
}

如何使用JSP / Servlet將文件上傳到服務器? 以及如何使用JSF 2.2 <h:inputFile>來上傳文件? 保存的文件在哪裡?

注意:不要使用Part#write()來解釋相對於@MultipartConfig(location)定義的臨時存儲位置的路徑。

也可以看看:




如果你想這樣做:

$.upload( form.action, new FormData( myForm))
.progress( function( progressEvent, upload) {
    if( progressEvent.lengthComputable) {
        var percent = Math.round( progressEvent.loaded * 100 / progressEvent.total) + '%';
        if( upload) {
            console.log( percent + ' uploaded');
        } else {
            console.log( percent + ' downloaded');
        }
    }
})
.done( function() {
    console.log( 'Finished upload');                    
});

https://github.com/lgersman/jquery.orangevolt-ampere/blob/master/src/jquery.upload.js

可能是你的解決方案。





tomcat servlets file-upload servlet-3.0