A maneira recomendada para salvar arquivos enviados em um aplicativo de servlet

Eu li aqui que não se deve salvar o arquivo no servidor, uma vez que não é portátil, transacional e requer parâmetros externos. No entanto, dado que eu preciso de uma solução tmp para o tomcat (7) e que eu tenho controle (relativo) sobre a máquina do servidor que eu quero saber:

Infelizmente, o FileServlet by @BalusC se concentra no download de arquivos, enquanto sua resposta ao fazer upload de arquivos ignora a parte sobre onde salvar o arquivo.

Uma solução facilmente conversível para usar uma implementação DB ou JCR (como jackrabbit ) seria preferível.

Armazene-o em qualquer lugar em um local acessível, exceto na pasta de projeto do IDE, também conhecida como pasta de implementação do servidor, por motivos mencionados na resposta para a imagem Carregada disponível somente após a atualização da página :

  1. As alterações na pasta do projeto do IDE não são refletidas imediatamente na pasta de trabalho do servidor. Há um tipo de trabalho em segundo plano no IDE que cuida para que a pasta de trabalho do servidor seja sincronizada com as últimas atualizações (isso é em termos do IDE chamado “publicação”). Esta é a principal causa do problema que você está vendo.

  2. No código do mundo real, há circunstâncias em que o armazenamento de arquivos enviados na pasta de implantação da webapp não funcionará. Alguns servidores (por padrão ou por configuração) não expandem o arquivo WAR implementado para o sistema de arquivos em disco local, mas, em vez disso, estão totalmente na memory. Você não pode criar novos arquivos na memory sem basicamente editar o arquivo WAR implementado e reimplementá-lo.

  3. Mesmo quando o servidor expande o arquivo WAR implementado no sistema de arquivos do disco local, todos os arquivos recém-criados serão perdidos em uma reimplantação ou até mesmo uma reboot simples, simplesmente porque esses novos arquivos não fazem parte do arquivo WAR original.

Realmente não importa para mim ou qualquer outra pessoa onde exatamente no sistema de arquivos do disco local ele será salvo, contanto que você nunca use o método getRealPath() . Usando esse método é em qualquer caso alarmante.

O caminho para o local de armazenamento, por sua vez, pode ser definido de várias maneiras. Você tem que fazer tudo sozinho . Talvez seja aqui que a sua confusão é causada porque você de alguma forma esperava que o servidor fizesse isso automaticamente. Observe que @MultipartConfig(location) não especifica o destino final do upload, mas o local de armazenamento temporário para o tamanho do arquivo de caso excede o limite de armazenamento de memory.

Assim, o caminho para o local de armazenamento final pode ser definido de uma das seguintes maneiras:

  • Codificado:

     File uploads = new File("/path/to/uploads"); 
  • Variável de ambiente via SET UPLOAD_LOCATION=/path/to/uploads :

     File uploads = new File(System.getenv("UPLOAD_LOCATION")); 
  • Argumento da VM durante a boot do servidor via -Dupload.location="/path/to/uploads" :

     File uploads = new File(System.getProperty("upload.location")); 
  • Entrada de arquivo *.properties como upload.location=/path/to/uploads :

     File uploads = new File(properties.getProperty("upload.location")); 
  • web.xml com nome upload.location e valor /path/to/uploads :

     File uploads = new File(getServletContext().getInitParameter("upload.location")); 
  • Se houver, use o local fornecido pelo servidor, por exemplo, no JBoss AS / WildFly :

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

De qualquer forma, você pode facilmente fazer referência e salvar o arquivo da seguinte maneira:

 File file = new File(uploads, "somefilename.ext"); try (InputStream input = part.getInputStream()) { Files.copy(input, file.toPath()); } 

Ou, quando você quiser gerar automaticamente um nome de arquivo exclusivo para impedir que os usuários substituam arquivos existentes coincidentemente com o mesmo nome:

 File file = File.createTempFile("somefilename-", ".ext", uploads); try (InputStream input = part.getInputStream()) { Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING); } 

Como obter part no JSP / Servlet é respondida em Como fazer upload de arquivos para o servidor usando JSP / Servlet? e como obter part no JSF é respondida em Como fazer o upload do arquivo usando o JSF 2.2 ? Onde está o arquivo salvo?

Nota: não use Part#write() pois ele interpreta o caminho relativo ao local de armazenamento temporário definido em @MultipartConfig(location) .

Veja também:

  • Como salvar o arquivo enviado no JSF (segmentado por JSF, mas o princípio é praticamente o mesmo)
  • Maneira mais simples de fornecer dados estáticos de fora do servidor de aplicativos em um aplicativo da Web Java (no caso de você desejar exibi-los de volta)
  • Como salvar o arquivo gerado temporariamente no aplicativo da web baseado em servlet

Eu publico minha maneira final de fazer isso com base na resposta aceita:

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

Onde :

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

e o /WEB-INF/app.properties:

 upload.location=C:/_/ 

HTH e se você encontrar um bug me avise