Zalecany sposób zapisywania przesłanych plików w aplikacji servlet

Czytałem tutaj, że nie należy zapisywać pliku na serwerze, ponieważ nie jest przenośny, transakcyjny i wymaga zewnętrznych parametrów. Jednak biorąc pod uwagę, że potrzebuję rozwiązania tmp dla tomcat (7) i że mam (względną) kontrolę nad maszyną serwerową, chcę wiedzieć :

  • Jakie jest najlepsze miejsce do zapisania pliku ? Należy zapisać go w /WEB-INF/uploads (odradzam tutaj) lub gdzieś pod $CATALINA_BASE (patrz tutaj ) lub ... ? JavaEE 6 tutorial pobiera ścieżkę od użytkownika (:wtf:). Uwaga: plik nie powinien być pobierany w żaden sposób.

  • Czy powinienem ustawić parametr config jako detailed tutaj ? Byłbym wdzięczny za jakiś kod (wolałbym dać mu ścieżkę względną-więc jest przynajmniej Tomcat portable) - Part.write() wygląda obiecująco - ale najwyraźniej potrzebuje ścieżki absolutnej

  • Byłbym zainteresowany przedstawieniem wad tego podejścia vs bazy danych / repozytorium JCR one

Niestety FileServlet autorstwa @BalusC koncentruje się na pobieraniu plików, podczas gdy jego odpowiedź przy przesyłaniu plików pomija część, gdzie zapisać plik.

Rozwiązanie łatwo konwertowalne do użycia implementacji DB lub JCR (Jak jackrabbit) byłoby preferowane.

Author: Community, 2013-09-06

2 answers

Przechowuj go w dowolnym miejscu w dostępnym miejscu z wyjątkiem folderu projektu IDE, czyli folderu wdrażania serwera, z powodów wymienionych w odpowiedzi na przesłany obraz dostępny tylko po odświeżeniu strony :

  1. Zmiany w folderze projektu IDE nie są natychmiast odzwierciedlane w folderze roboczym serwera. W IDE jest rodzaj zadania w tle, które dba o to, aby folder roboczy serwera został zsynchronizowany z ostatnimi aktualizacjami (jest to w pojęciach IDE nazywane "publikowaniem"). To jest główna przyczyna problemu, który widzisz.

  2. W kodzie świata rzeczywistego istnieją okoliczności, w których przechowywanie przesłanych plików w folderze wdrożenia webapp w ogóle nie będzie działać. Niektóre serwery (domyślnie lub według konfiguracji) nie rozszerzają wdrożonego pliku wojennego do lokalnego systemu plików na dysku, ale w pełni w pamięci. Nie można tworzyć nowych plików w pamięci bez edycji pliku WAR i przesunięcie.

  3. Nawet gdy serwer rozszerza wdrożony plik wojenny do systemu plików na dysku lokalnym, wszystkie nowo utworzone pliki zostaną utracone przy ponownym uruchomieniu lub nawet prostym ponownym uruchomieniu, po prostu dlatego, że te nowe pliki nie są częścią oryginalnego pliku wojennego.

To naprawdę nie ma znaczenia dla mnie lub kogokolwiek innego, gdzie dokładnie na lokalnym systemie plików dysk zostanie zapisany, tak długo, jak Nie Nie kiedykolwiek używać getRealPath() metoda. Używając tego metoda jest w dowolny przypadek alarmujący.

Ścieżka do miejsca przechowywania może z kolei być definiowana na wiele sposobów. Musisz zrobić to wszystko przez siebie. Być może to jest miejsce, w którym Twoje zamieszanie jest spowodowane, ponieważ w jakiś sposób spodziewałeś się, że serwer robi to wszystko automagicznie. Należy pamiętać, że @MultipartConfig(location) nie określa miejsca docelowego ostatecznego przesłania, ale miejsce tymczasowego przechowywania dla rozmiaru pliku sprawy przekracza próg przechowywania pamięci.

Więc droga do ostateczne miejsce przechowywania można zdefiniować na jeden z następujących sposobów:

  • Hardcoded:

    File uploads = new File("/path/to/uploads");
    
  • Zmienna środowiskowa via SET UPLOAD_LOCATION=/path/to/uploads:

    File uploads = new File(System.getenv("UPLOAD_LOCATION"));
    
  • Argument VM podczas uruchamiania serwera poprzez -Dupload.location="/path/to/uploads":

    File uploads = new File(System.getProperty("upload.location"));
    
  • *.properties wpis pliku jako upload.location=/path/to/uploads:

    File uploads = new File(properties.getProperty("upload.location"));
    
  • web.xml <context-param> z nazwą upload.location i wartością /path/to/uploads:

    File uploads = new File(getServletContext().getInitParameter("upload.location"));
    
  • Jeśli istnieje, użyj lokalizacji podanej przez serwer, np. w JBoss AS / WildFly :

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

Tak czy inaczej, możesz łatwo odwołać się i zapisać plik w następujący sposób:

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

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

Lub, gdy chcesz autogenerować unikalną nazwę pliku, aby uniemożliwić użytkownikom nadpisanie istniejących plików o przypadkowo tej samej nazwie:

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

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

Jak uzyskać part w JSP/Servlet znajdziesz odpowiedź w jak wgrać pliki na serwer używając JSP/Servlet? i jak uzyskać part w JSF odpowiem w jak wgrać plik używając JSF 2.2 ? Gdzie jest zapisany plik?

Uwaga: do Nie użyj Part#write() interpretuje ścieżkę względem tymczasowego miejsca przechowywania zdefiniowanego w @MultipartConfig(location).

Zobacz też:

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:50

Zamieszczam swój ostateczny sposób na zrobienie tego na podstawie zaakceptowanej odpowiedzi:

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

Gdzie:

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

I / WEB-INF / app.właściwości:

upload.location=C:/_/

HTH i jeśli znajdziesz błąd daj mi znać

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-17 12:36:07