Yüklenen dosyaları sunucu uygulaması uygulamasına kaydetmenin önerilen yolu


121

Burada , taşınabilir, işlemsel olmadığı ve harici parametreler gerektirdiği için dosyanın sunucuya kaydedilmemesi gerektiğini okudum . Bununla birlikte, tomcat (7) için bir tmp çözümüne ihtiyacım olduğu ve bilmek istediğim sunucu makinesi üzerinde (göreceli) kontrole sahip olduğum göz önüne alındığında:

  • Dosyayı kaydetmek için en iyi yer neresidir? Onu saklamalı mıyım /WEB-INF/uploads( burada tavsiye edilmiyor) veya altında bir yere $CATALINA_BASE( buraya bakın ) veya ...? JavaEE 6 öğreticisi , kullanıcının yolunu (: wtf :) alır. Not: Dosya hiçbir şekilde indirilebilir olmamalıdır.

  • Burada ayrıntıları verildiği gibi bir yapılandırma parametresi oluşturmalı mıyım ? Bazı kodları takdir ediyorum (ona göreceli bir yol vermeyi tercih ederim - yani en azından Tomcat taşınabilir) - Part.write()umut verici görünüyor - ama görünüşe göre mutlak bir yola ihtiyacı var

  • Bu yaklaşımın bir veritabanı / JCR deposuna kıyasla dezavantajlarının bir açıklamasıyla ilgilenirim.

Maalesef @BalusC'nin FileServlet'i dosyaları indirmeye odaklanırken, dosya yükleme konusundaki cevabı dosyanın nereye kaydedileceğini atlar.

Bir DB veya JCR uygulaması ( jackrabbit gibi ) kullanmak için kolayca dönüştürülebilen bir çözüm tercih edilebilir.


Bunu yapmanın son yolu için aşağıdaki cevaba bakın
Mr_and_Mrs_D

Yanıtlar:


165

Yüklenen görüntü yanıtında belirtilen nedenlerle, yalnızca sayfayı yeniledikten sonra kullanılabilir olan, IDE'nin proje klasörü, yani sunucunun dağıtım klasörü dışında , erişilebilir bir konumda herhangi bir yerde saklayın :

  1. IDE'nin proje klasöründeki değişiklikler, sunucunun çalışma klasörüne hemen yansıtılmaz. IDE'de, sunucunun çalışma klasörünün son güncellemelerle senkronize edilmesini sağlayan bir tür arka plan işi vardır (bu, "yayınlama" olarak adlandırılan IDE terimleridir). Gördüğünüz sorunun ana nedeni budur.

  2. Gerçek dünya kodunda, yüklenen dosyaların web uygulamasının dağıtım klasöründe saklanmasının hiç çalışmayacağı durumlar vardır. Bazı sunucular (varsayılan olarak veya yapılandırmaya göre) dağıtılan WAR dosyasını yerel disk dosya sistemine değil, tam olarak belleğe genişletebilir. Temel olarak konuşlandırılmış WAR dosyasını düzenlemeden ve yeniden dağıtmadan bellekte yeni dosyalar oluşturamazsınız.

  3. Sunucu konuşlandırılmış WAR dosyasını yerel disk dosya sistemine genişlettiğinde bile, yeni oluşturulan tüm dosyalar bir yeniden dağıtımda veya hatta basit bir yeniden başlatmada kaybolur, çünkü bu yeni dosyalar orijinal WAR dosyasının bir parçası değildir.

Benim ya da başka herhangi birinin, sizin yaptığınız sürece, yerel disk dosya sisteminde tam olarak nerede kaydedileceği gerçekten önemli değil değil hiç kullanmak getRealPath()yöntemini . Bu yöntemi kullanmak herhangi bir durumda endişe vericidir.

Depolama konumuna giden yol, sırayla birçok şekilde tanımlanabilir. Hepsini yapmak zorundasın kendin . Belki de kafa karışıklığınızın nedeni budur çünkü bir şekilde sunucunun tüm bunları otomatik olarak yapmasını beklemiştiniz. Unutmayın ki @MultipartConfig(location)yok değil nihai yükleme hedefi belirtin, ancak dava dosya boyutu için geçici depolama yeri bellek depolama eşiğini aşıyor.

Bu nedenle, son depolama konumuna giden yol, aşağıdaki yollardan biriyle tanımlanabilir:

  • kodlanmış:

      File uploads = new File("/path/to/uploads");
  • Aracılığıyla ortam değişkeni SET UPLOAD_LOCATION=/path/to/uploads :

      File uploads = new File(System.getenv("UPLOAD_LOCATION"));
  • Sunucu başlatılırken VM argümanı -Dupload.location="/path/to/uploads":

      File uploads = new File(System.getProperty("upload.location"));
  • *.propertiesdosya girişi upload.location=/path/to/uploads:

      File uploads = new File(properties.getProperty("upload.location"));
  • web.xml <context-param>isim upload.locationve değer ile/path/to/uploads :

      File uploads = new File(getServletContext().getInitParameter("upload.location"));
  • Varsa, sunucu tarafından sağlanan konumu kullanın, örneğin JBoss AS / WildFly :

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

Her iki durumda da, dosyaya aşağıdaki gibi kolayca başvurabilir ve kaydedebilirsiniz:

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

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

Veya kullanıcıların tesadüfen aynı adı taşıyan mevcut dosyaların üzerine yazmasını önlemek için benzersiz bir dosya adını otomatik olarak oluşturmak istediğinizde:

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

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

partJSP / Servlet'te nasıl edinileceği JSP / Servlet kullanılarak sunucuya nasıl dosya yüklenir? ve nasıl elde edilirpart JSF cevaplanır <: InputFile h> JSF 2.2 kullanarak dosya yükleme nasıl? Kaydedilen Dosya nerede?

Not: do not kullanmakPart#write() o tanımlanan geçici depolama konumuna göre yolu yorumlamaktadır olarak @MultipartConfig(location).

Ayrıca bakınız:


@MultipartConfig(location)Belirtir geçici dosya boyutu bellek depolama için eşiği aştığında sunucu kullanmalıdır storge konumu, sen sonuçta böyle olur değil kalıcı depolama yeri saklanacak. Bu değer varsayılan olarak java.io.tmpdirsistem özelliği tarafından tanımlanan yolu belirtir . Başarısız bir JSF girişimine ilişkin şu ilgili yanıta da bakın: stackoverflow.com/questions/18478154/…
BalusC

1
Teşekkürler - umarım aptalca görünmüyorum ama bu alıntı Part.write>> Bu, belirli bir uygulamanın, örneğin, tüm temel verileri kopyalamak yerine, mümkün olduğunda dosya yeniden adlandırmasını kullanmasına izin verir, böylece bazılarıyla bağlantılı olarak önemli bir performans avantajı elde eder. bazı apache kütüphanelerinin bilinmeyen "cut" (vs copy) yöntemi, beni baytları kendim yazma ve zaten orada bir dosyayı yeniden oluşturma zahmetinden kurtarırdı (ayrıca buraya bakın )
Mr_and_Mrs_D

Evet, zaten Servlet 3.0 üzerindeyseniz, yararlanabilirsiniz Part#write(). Cevabı onunla güncelledim.
BalusC

Gönderiyi güncel tuttuğunuz için çok teşekkür ederiz - Tomcat için böyle bir mülk var "jboss.server.data.dir"mı?
Mr_and_Mrs_D

1
Hayır, yok.
BalusC

7

Kabul edilen cevaba göre bunu yapmanın son yolunu gönderiyorum:

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

nerede :

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

ve /WEB-INF/app.properties:

upload.location=C:/_/

HTH ve bir hata bulursanız bana bildirin


1
Ya her iki durumda da (win / ux) çalışan SO bağımsız bir çözüm istersem? Farklı upload.location yolu ayarlamam gerekiyor mu yoksa başka bir ipucu var mı?
pikimota
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.