Forma recomendada de guardar los archivos cargados en una aplicación servlet


He leído aquí que uno no debe guardar el archivo en el servidor de todos modos, ya que no es portátil, transaccional y requiere parámetros externos. Sin embargo, dado que necesito una solución tmp para tomcat (7) y que tengo un control (relativo) sobre la máquina servidor, quiero saber:

  • ¿Cuál es el mejor lugar para guardar el archivo ? Debería guardarlo en /WEB-INF/uploads (desaconsejado aquí ) o en algún lugar debajo de $CATALINA_BASE (ver aquí ) o ... ? El tutorial de JavaEE 6 obtiene la ruta del usuario (: wtf:). NOTA: El archivo no debe ser descargable por ningún medio.

  • ¿Debo configurar un parámetro de configuración como se detalla aquí ? Apreciaría un poco de código (prefiero darle una ruta relativa, por lo que es al menos portátil Tomcat) - Part.write() parece prometedor, pero aparentemente necesita un camino absoluto

  • Me interesaría una exposición de las desventajas de este enfoque frente a un repositorio de base de datos / JCR uno

Desafortunadamente el FileServlet de @BalusC se concentra en descargar archivos, mientras que su respuesta en subir archivos omite la parte sobre dónde guardar el archivo.

Una solución fácilmente convertible para usar una implementación DB o JCR (como jackrabbit) sería preferible.

Author: Community, 2013-09-06

2 answers

Almacénelo en cualquier lugar en una ubicación accesible excepto de la carpeta de proyecto del IDE, también conocida como la carpeta de despliegue del servidor, por las razones mencionadas en la respuesta a La imagen cargada solo está disponible después de actualizar la página :

  1. Los cambios en la carpeta de proyecto del IDE no se reflejan inmediatamente en la carpeta de trabajo del servidor. Hay una especie de trabajo en segundo plano en el IDE que se encarga de que la carpeta de trabajo del servidor se sincronice con las últimas actualizaciones (esto es en términos IDE llamado "publicación"). Esta es la causa principal del problema que estás viendo.

  2. En el código del mundo real hay circunstancias en las que el almacenamiento de archivos cargados en la carpeta de implementación de la aplicación web no funcionará en absoluto. Algunos servidores (ya sea de forma predeterminada o por configuración) no expanden el archivo WAR implementado en el sistema de archivos de disco local, sino completamente en la memoria. No puede crear nuevos archivos en la memoria sin editar básicamente el archivo WAR desplegado y redistribuyéndolo.

  3. Incluso cuando el servidor expande el archivo WAR implementado en el sistema de archivos de disco local, todos los archivos recién creados se perderán en una redistribución o incluso en un simple reinicio, simplemente porque esos archivos nuevos no son parte del archivo WAR original.

Realmente no me importa a mí ni a nadie dónde exactamente en el sistema de archivos de disco local se guardará, siempre y cuando haga no nunca use el método getRealPath() . Usando eso el método está en cualquier caso alarmante.

La ruta a la ubicación de almacenamiento a su vez se puede definir de muchas maneras. Tienes que hacerlo todo por tú mismo . Tal vez aquí es donde se causa su confusión porque de alguna manera esperaba que el servidor lo haga todo automáticamente. Tenga en cuenta que @MultipartConfig(location) no especifica el destino final de carga, pero la ubicación de almacenamiento temporal para el tamaño del archivo de caso supera el umbral de almacenamiento de memoria.

Entonces, el camino a la ubicación de almacenamiento final se puede definir de cualquiera de las siguientes maneras:

  • Codificado:

    File uploads = new File("/path/to/uploads");
    
  • Variable de entorno via SET UPLOAD_LOCATION=/path/to/uploads:

    File uploads = new File(System.getenv("UPLOAD_LOCATION"));
    
  • Argumento de VM durante el inicio del servidor a través de -Dupload.location="/path/to/uploads":

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

    File uploads = new File(properties.getProperty("upload.location"));
    
  • web.xml <context-param> con el nombre upload.location y el valor /path/to/uploads:

    File uploads = new File(getServletContext().getInitParameter("upload.location"));
    
  • Si lo hay, utilice la ubicación proporcionada por el servidor, por ejemplo, en JBoss AS / WildFly :

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

De cualquier manera, puede hacer referencia y guardar fácilmente el archivo de la siguiente manera:

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

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

O, cuando desee generar automáticamente un nombre de archivo único para evitar que los usuarios sobrescriban archivos existentes con coincidentemente el mismo nombre:

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

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

Cómo obtener part en JSP/Servlet se responde en Cómo subir archivos al servidor usando JSP/Servlet? y cómo obtener part en JSF se responde en Cómo subir un archivo usando JSF 2.2 ? ¿Dónde está el archivo guardado?

Nota: usar no Part#write() como interpreta la ruta relativa a la ubicación de almacenamiento temporal definida en @MultipartConfig(location).

Véase también:

 138
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:50

Posteo mi forma final de hacerlo basado en la respuesta aceptada:

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

Donde:

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

Y /WEB-INF/app.propiedades :

upload.location=C:/_/

HTH y si encuentras un error házmelo saber

 6
Author: Mr_and_Mrs_D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-17 12:36:07