tomcat - Forma recomendada para guardar archivos cargados en una aplicación servlet
servlets file-upload (2)
Leí here 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 el control (relativo) de la máquina servidor, quiero saber:
¿Cuál es el mejor lugar para guardar el archivo? ¿Debo guardarlo en
/WEB-INF/uploads
(desaconsejado here ) o en algún lugar por debajo de$CATALINA_BASE
(ver here ) o ...? El tutorial de JavaEE 6 obtiene la ruta del usuario (: wtf :). Nota: el archivo no debe poder descargarse de ninguna manera.¿Debo configurar un parámetro de configuración como se detalla here ? Apreciaría un poco de código (prefiero darle una ruta relativa, por lo que es al menos portátil de Tomcat).
Part.write()
parece prometedor, pero aparentemente necesita una ruta absoluta.Me interesaría una exposición de las desventajas de este enfoque frente a uno de repositorio de base de datos / JCR
Desafortunadamente, el FileServlet de @BalusC se concentra en la descarga de archivos, mientras que su answer sobre la carga de 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.
Guárdelo en cualquier lugar en una ubicación accesible, excepto en la carpeta del proyecto del IDE, también conocida como la carpeta de implementación del servidor, por los motivos mencionados en la respuesta a la imagen cargada solo disponible después de actualizar la página :
Los cambios en la carpeta del proyecto del IDE no se reflejan inmediatamente en la carpeta de trabajo del servidor. Hay una especie de trabajo de fondo en el IDE que se ocupa de que la carpeta de trabajo del servidor se sincronice con las últimas actualizaciones (esto es en términos IDE llamados "publicación"). Esta es la causa principal del problema que estás viendo.
En el código del mundo real existen circunstancias en las que el almacenamiento de los archivos cargados en la carpeta de implementación de la aplicación web no funcionará en absoluto. Algunos servidores (no de forma predeterminada o por configuración) no expanden el archivo WAR implementado en el sistema de archivos del disco local, sino que lo almacenan completamente en la memoria. No puede crear nuevos archivos en la memoria sin básicamente editar el archivo WAR desplegado y volver a implementarlo.
Incluso cuando el servidor expande el archivo WAR desplegado en el sistema de archivos del disco local, todos los archivos recién creados se perderán en una reimplementación o incluso un simple reinicio, simplemente porque esos nuevos archivos no son parte del archivo WAR original.
Realmente no nos importa ni a mí ni a nadie dónde se guardará exactamente en el sistema de archivos del disco local, siempre que no utilice el método getRealPath()
. Usar ese método es en cualquier caso alarmante.
La ruta a la ubicación de almacenamiento puede definirse a su vez de muchas maneras. Tienes que hacerlo todo por ti mismo . Quizás aquí es donde se genera su confusión porque de alguna manera se espera que el servidor lo haga automágicamente. Tenga en cuenta que @MultipartConfig(location)
no especifica el destino de carga final, pero la ubicación de almacenamiento temporal para el tamaño del archivo de caso excede el límite de almacenamiento de memoria.
Por lo tanto, la ruta a la ubicación de almacenamiento final se puede definir de cualquiera de las siguientes maneras:
Hardcoded:
File uploads = new File("/path/to/uploads");
Variable de entorno a través de
SET UPLOAD_LOCATION=/path/to/uploads
:File uploads = new File(System.getenv("UPLOAD_LOCATION"));
Argumento VM durante el inicio del servidor mediante
-Dupload.location="/path/to/uploads"
:File uploads = new File(System.getProperty("upload.location"));
*.properties
file entry comoupload.location=/path/to/uploads
:File uploads = new File(properties.getProperty("upload.location"));
web.xml
<context-param>
con el nombreupload.location
y value/path/to/uploads
:File uploads = new File(getServletContext().getInitParameter("upload.location"));
Si hay alguno, use 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 fácilmente y guardar el archivo de la siguiente manera:
File file = new File(uploads, "somefilename.ext");
try (InputStream input = part.getInputStream()) {
Files.copy(input, file.toPath());
}
O bien, cuando desee generar automáticamente un nombre de archivo exclusivo para evitar que los usuarios sobrescriban archivos existentes con el mismo nombre coincidente:
File file = File.createTempFile("somefilename-", ".ext", uploads);
try (InputStream input = part.getInputStream()) {
Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
¿Cómo obtener una part
en JSP / Servlet se responde en Cómo subir archivos al servidor usando JSP / Servlet? y cómo obtener una part
en JSF se responde en Cómo subir un archivo usando JSF 2.2 <h: inputFile>? ¿Dónde está el archivo guardado?
Nota: no utilice Part#write()
ya que interpreta la ruta relativa a la ubicación de almacenamiento temporal definida en @MultipartConfig(location)
.
Ver también:
- Cómo guardar el archivo cargado en JSF (JSF-targeted, pero el principio es prácticamente el mismo)
- La forma más simple de servir datos estáticos desde fuera del servidor de aplicaciones en una aplicación web Java (en caso de que quiera volver a publicarla)
- Cómo guardar el archivo generado temporalmente en una aplicación web basada en servlet
Publico mi forma final de hacerlo en base a 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://.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;
}
}
dónde :
@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 las /WEB-INF/app.properties:
upload.location=C:/_/
HTH y si encuentra un error, hágamelo saber