127 votes

Méthode recommandée pour sauvegarder les fichiers téléchargés dans une application servlet

Je lis ici qu'il ne faut de toute façon pas enregistrer le fichier sur le serveur car il n'est pas portable, transactionnel et nécessite des paramètres externes. Cependant, étant donné que j'ai besoin d'une solution tmp pour tomcat (7) et que j'ai un contrôle (relatif) sur la machine serveur, je veux savoir :

  • Quel est le meilleur endroit pour sauvegarder le fichier ? Dois-je le sauvegarder dans /WEB-INF/uploads (déconseillé ici ) ou quelque part sous $CATALINA_BASE (ver ici ) ou ... ? Le tutoriel JavaEE 6 obtient le chemin de l'utilisateur (:wtf :). NB : Le fichier ne doit pas être téléchargeable par quelque moyen que ce soit.

  • Dois-je configurer un paramètre de configuration comme détaillé ici ? J'apprécierais un peu de code (je préfèrerais lui donner un chemin relatif - pour qu'il soit au moins portable avec Tomcat). Part.write() semble prometteur - mais apparemment, il faut un chemin absolu

  • Je serais intéressé par un exposé des inconvénients de cette approche par rapport à celle d'un dépôt de base de données/JCR.

Malheureusement, le FileServlet par @BalusC se concentre sur le téléchargement de fichiers, tandis que son réponse sur le téléchargement de fichiers saute la partie sur l'endroit où enregistrer le fichier.

Une solution facilement convertible consiste à utiliser une DB ou une implémentation JCR (comme jackrabbit ) serait préférable.

172voto

BalusC Points 498232

Stockez-le n'importe où dans un endroit accessible sauf du dossier de projet de l'IDE vers le dossier de déploiement du serveur, pour les raisons mentionnées dans la réponse à la question suivante L'image téléchargée n'est disponible qu'après avoir rafraîchi la page :

  1. Les modifications apportées au dossier de projet de l'IDE ne sont pas immédiatement répercutées dans le dossier de travail du serveur. Il y a une sorte de tâche de fond dans l'IDE qui s'occupe de synchroniser le dossier de travail du serveur avec les dernières mises à jour (en termes d'IDE, cela s'appelle "publication"). C'est la principale cause du problème que vous rencontrez.

  2. Dans le code du monde réel, il existe des circonstances où le stockage des fichiers téléchargés dans le dossier de déploiement de la webapp ne fonctionne pas du tout. Certains serveurs (par défaut ou par configuration) ne développent pas le fichier WAR déployé dans le système de fichiers du disque local, mais entièrement dans la mémoire. Vous ne pouvez pas créer de nouveaux fichiers dans la mémoire sans modifier le fichier WAR déployé et le redéployer.

  3. Même lorsque le serveur étend le fichier WAR déployé dans le système de fichiers du disque local, tous les fichiers nouvellement créés seront perdus lors d'un redéploiement ou même d'un simple redémarrage, simplement parce que ces nouveaux fichiers ne font pas partie du fichier WAR d'origine.

Peu importe pour moi ou pour quiconque l'endroit exact du système de fichiers du disque local où il sera sauvegardé, tant que vous faire no jamais utilisé getRealPath() méthode . L'utilisation de cette méthode est en tout cas alarmant.

Le chemin vers l'emplacement de stockage peut à son tour être défini de plusieurs façons. Vous devez faire tout cela en vous-même . C'est peut-être là que se situe votre confusion, car vous vous attendiez à ce que le serveur fasse tout cela automatiquement. Veuillez noter que @MultipartConfig(location) fait no spécifier la destination finale du téléchargement, mais l'emplacement de stockage temporaire pour le cas où la taille du fichier dépasse le seuil de stockage en mémoire.

Ainsi, le chemin vers l'emplacement de stockage final peut être défini de l'une des manières suivantes :

  • Hardcoded :

      File uploads = new File("/path/to/uploads");
  • Variable d'environnement via SET UPLOAD_LOCATION=/path/to/uploads :

      File uploads = new File(System.getenv("UPLOAD_LOCATION"));
  • Argument de la VM lors du démarrage du serveur via -Dupload.location="/path/to/uploads" :

      File uploads = new File(System.getProperty("upload.location"));
  • *.properties l'entrée du fichier en tant que upload.location=/path/to/uploads :

      File uploads = new File(properties.getProperty("upload.location"));
  • web.xml <context-param> avec le nom upload.location et la valeur /path/to/uploads :

      File uploads = new File(getServletContext().getInitParameter("upload.location"));
  • S'il y en a, utilisez l'emplacement fourni par le serveur, par exemple dans JBoss AS/WildFly :

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

Dans tous les cas, vous pouvez facilement référencer et enregistrer le fichier comme suit :

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

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

Ou lorsque vous souhaitez générer automatiquement un nom de fichier unique pour empêcher les utilisateurs d'écraser des fichiers existants portant par coïncidence le même nom :

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

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

Comment obtenir part dans JSP/Servlet est répondu dans Comment télécharger des fichiers vers le serveur en utilisant JSP/Servlet ? et comment obtenir part dans JSF est répondu dans Comment télécharger un fichier en utilisant JSF 2.2 <h:inputFile> ? Où se trouve le fichier sauvegardé ?

Note : ne no utiliser Part#write() car il interprète le chemin d'accès relatif à l'emplacement de stockage temporaire défini dans le fichier @MultipartConfig(location) . Assurez-vous également que vous n'êtes pas en train de corrompre des fichiers binaires tels que des fichiers PDF ou des fichiers d'images en convertissant des octets en caractères pendant la lecture/écriture en utilisant de manière incorrecte un fichier de type Reader / Writer au lieu de InputStream / OutputStream .

Voir aussi :

7voto

Mr_and_Mrs_D Points 4569

J'affiche ma façon finale de procéder, basée sur la réponse acceptée :

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

où :

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

et le fichier /WEB-INF/app.properties :

upload.location=C:/_/

HTH et si vous trouvez un bug, faites-le moi savoir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X