125

我在这里读到,无论如何都不应该将文件保存在服务器中,因为它不可移植、事务性且需要外部参数。但是,鉴于我需要一个用于 tomcat (7) 的 tmp 解决方案,并且我对我想知道的服务器机器具有(相对)控制权:

  • 保存文件的最佳位置是什么?我应该把它保存在/WEB-INF/uploads(建议不要在这里)还是下面的某个地方$CATALINA_BASE(见这里)或...?JavaEE 6 教程从用户处获取路径(:wtf:)。注意:该文件不应以任何方式下载。

  • 我应该在这里详细设置配置参数吗?我会很感激一些代码(我宁愿给它一个相对路径 - 所以它至少是 Tomcat 可移植的) -Part.write()看起来很有希望 - 但显然需要一个绝对路径

  • 我有兴趣了解这种方法与数据库/JCR 存储库的缺点

不幸的是,@BalusC 的FileServlet 专注于下载文件,而他关于上传文件的回答跳过了保存文件的部分。

一个易于转换为使用 DB 或 JCR 实现(如jackrabbit)的解决方案将是更可取的。

4

2 回答 2

170

将其存储在除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);
}

如何使用 JSP/Servlet 将文件上传到服务器part中回答了如何在 JSP/Servlet 中获取?How to upload file using JSF 2.2 <h:inputFile>?中回答了如何在 JSF 中获取。保存的文件在哪里?part

注意:请勿使用Part#write()因为它会解释相对于@MultipartConfig(location). 还要确保在读/写期间通过错误地使用Reader/Writer而不是InputStream/将字节转换为字符,从而不会损坏二进制文件,例如 PDF 文件或图像文件OutputStream

也可以看看:

于 2013-09-06T18:58:46.917 回答
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,如果您发现错误,请告诉我

于 2016-03-17T12:36:07.757 回答