web-dev-qa-db-fra.com

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

J'ai lu ici qu'il ne faut de toute façon pas sauvegarder 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 le contrôle (relatif) de la machine serveur, je veux savoir:

  • Quel est le meilleur endroit pour enregistrer le fichier? Dois-je l'enregistrer dans /WEB-INF/uploads (déconseillé ici ) ou quelque part sous $CATALINA_BASE (voir ici ) ou ...? Le tutoriel JavaEE 6 obtient le chemin de l'utilisateur (: wtf :). NB: le fichier ne doit en aucun cas être téléchargeable.

  • Devrais-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 - il est donc au moins portable Tomcat) - Part.write() semble prometteur - mais a apparemment besoin d'un chemin absolu

  • Je serais intéressé par une présentation des inconvénients de cette approche par rapport à une base de données/référentiel JCR

Malheureusement, le FileServlet de @BalusC se concentre sur le téléchargement de fichiers, tandis que son réponse sur le téléchargement de fichiers ignore la partie sur laquelle enregistrer le fichier.

Une solution facilement convertible pour utiliser une base de données ou une implémentation JCR (comme jackrabbit ) serait préférable.

116
Mr_and_Mrs_D

Stockez-le n'importe où dans un emplacement accessible à l'exception de du dossier de projet de l'EDI, à savoir le dossier de déploiement du serveur, pour les raisons mentionnées dans la réponse à Image téléchargée disponible uniquement après avoir actualisé la page :

  1. Les modifications apportées au dossier de projet de l'EDI ne sont pas immédiatement reflétées dans le dossier de travail du serveur. Il y a une sorte de tâche en arrière-plan dans le IDE qui veille à ce que le dossier de travail du serveur soit synchronisé avec les dernières mises à jour (il s'agit de IDE termes appelés "publication"). C’est la principale cause du problème que vous rencontrez.

  2. Dans le code du monde réel, le stockage de fichiers téléchargés dans le dossier de déploiement de l'application Web 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 complètement dans la mémoire. Vous ne pouvez pas créer de nouveaux fichiers dans la mémoire sans éditer fondamentalement le fichier WAR déployé et le redéployer.

  3. Même lorsque le serveur étend le fichier WAR déployé au 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, tout simplement parce que ces nouveaux fichiers ne font pas partie du fichier WAR d'origine.

Cela n’a vraiment aucune importance pour moi ou pour qui que ce soit d’autre où exactement sur le système de fichiers du disque local il sera sauvegardé, aussi longtemps que vous do pas a déjà utilisé la méthode getRealPath() . Utiliser cette méthode est dans le cas any alarmant.

Le chemin d'accès à l'emplacement de stockage peut à son tour être défini de nombreuses manières. Vous devez tout faire par vous-même. C'est peut-être là que votre confusion est causée par le fait que vous vous attendiez d'une manière ou d'une autre à ce que le serveur le fasse automatiquement. Veuillez noter que @MultipartConfig(location) ne ne spécifie pas la destination de téléchargement finale, mais que l'emplacement de stockage temporaire pour la taille du fichier de dossier dépasse le seuil de mémoire.

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

  • Codé en dur:

    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 machine virtuelle lors du démarrage du serveur via -Dupload.location="/path/to/uploads":

    File uploads = new File(System.getProperty("upload.location"));
    
  • Entrée de fichier *.properties 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"));
    
  • Le cas échéant, 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 les deux 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 afin d'empêcher les utilisateurs d'écraser des fichiers existants portant le même nom par coïncidence:

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 sur un serveur à l'aide de JSP/Servlet? et comment obtenir part dans JSF est répondu dans Comment importer un fichier avec JSF 2.2 <h: inputFile>? Où se trouve le fichier enregistré?

Remarque: ne pas utilisez Part#write() car il interprète le chemin relatif à l'emplacement de stockage temporaire défini dans @MultipartConfig(location).

Voir également:

155
BalusC

Je publie ma dernière façon de le faire en fonction de 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 /WEB-INF/app.properties:

upload.location=C:/_/

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

7
Mr_and_Mrs_D