tomcat 在servlet应用程序中保存上传文件的推荐方法

zhte4eai  于 2023-11-19  发布在  其他
关注(0)|答案(3)|浏览(103)

我读到here,无论如何都不应该将文件保存在服务器上,因为它不是可移植的,事务性的,需要外部参数。然而,考虑到我需要一个针对tomcat(7)的tmp解决方案,并且我对服务器机器有(相对)控制权,我想知道:

  • 保存文件的最佳位置是什么?我应该保存它在/WEB-INF/uploads(建议不要here)或$CATALINA_BASE下的某个地方(请参阅here)或.?JavaEE 6教程从用户(:wtf:)获取路径。注意:该文件不应该以任何方式下载。
  • 我应该设置一个配置参数作为详细的here吗?我希望有一些代码(我宁愿给予它一个相对路径-所以它至少是Tomcat可移植的)-Part.write()看起来很有希望-但显然需要一个绝对路径
  • 我对这种方法相对于数据库/JCR存储库的缺点很感兴趣

不幸的是,@BalusC的FileServlet专注于下载文件,而他的answer关于上传文件的部分跳过了在哪里保存文件的部分。
一个易于转换为使用DB或JCR实现(如jackrabbit)的解决方案将是首选。

0lvr5msh

0lvr5msh1#

将其存储在IDE的项目文件夹(即服务器的部署文件夹)**以外的任何可访问位置,原因在Uploaded image only available after refreshing the page的回答中提到:

  1. IDE的项目文件夹中的更改不会立即反映到服务器的工作文件夹中。IDE中有一种后台作业,负责将服务器的工作文件夹与上次更新同步(在IDE中称为“发布”)。这是您看到的问题的主要原因。
    1.在真实的代码中,有些情况下,将上传的文件存储在webapp的deploy文件夹中根本不起作用。有些服务器(默认或配置)不会将部署的WAR文件扩展到本地磁盘文件系统中,而是完全在内存中。如果不编辑部署的WAR文件并重新部署它,就无法在内存中创建新文件。
    1.即使服务器将部署的WAR文件扩展到本地磁盘文件系统中,所有新创建的文件也会在重新部署或甚至简单的重新启动时丢失,这仅仅是因为这些新文件不是原始WAR文件的一部分。
    只要你do not ever use getRealPath() method,它将被保存在本地磁盘文件系统的哪个位置对我或其他任何人来说都不重要。
    到存储位置的路径可以通过多种方式定义。你必须自己完成。也许这就是你的困惑所在,因为你希望服务器自动完成这一切。请注意,@MultipartConfig(location)并不**指定最终上传目的地,但案例文件大小的临时存储位置超过了内存存储阈值。
    因此,最终存储位置的路径可以通过以下方式之一定义:
  • 硬编码:
File uploads = new File("/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,值为/path/to/uploads
File uploads = new File(getServletContext().getInitParameter("upload.location"));

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中如何获取partHow to upload files to server using JSP/Servlet?中回答,JSF中如何获取partHow to upload file using JSF 2.2 <h:inputFile>? Where is the saved File?中回答
注意事项:* * 不要**使用Part#write(),因为它解释相对于@MultipartConfig(location)中定义的临时存储位置的路径。另外,请绝对确保您没有在阅读/写过程中通过错误地使用Reader/Writer而不是InputStream/OutputStream将字节转换为字符来损坏二进制文件,如PDF文件或图像文件。

另请参阅:

chy5wohz

chy5wohz2#

我根据接受的答案发布我的最终方法:

@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:/_/


如果你发现一个bug,让我知道

nuypyhwy

nuypyhwy3#

Servlet规范为每个部署的Web应用程序提供了一个临时物理文件存储空间。您应该将其用于此目的。例如,当使用multipart-form-upload时:

Part part = request.getPart("param-name");
File tmpDir = (File)getServletContext().getAttribute("javax.servlet.context.tempdir");
File targetFile = new File(tmpDir, "tmp-file-name");
part.write(targetFile.getAbsolutePath());

字符串
现在,你可以对targetFile做任何你想做的事情。

相关问题