सर्वलेट एप्लिकेशन में अपलोड की गई फ़ाइलों को सहेजने का अनुशंसित तरीका


121

मैंने यहाँ पढ़ा कि किसी को भी सर्वर में फाइल सेव नहीं करनी चाहिए क्योंकि यह पोर्टेबल, ट्रांजेक्शनल नहीं है और इसके लिए बाहरी मापदंडों की आवश्यकता होती है। हालाँकि, यह देखते हुए कि मुझे tomcat (7) के लिए एक tmp समाधान की आवश्यकता है और जो मुझे (रिश्तेदार) नियंत्रण उस सर्वर मशीन पर है जिसे मैं जानना चाहता हूं:

  • फाइल को सेव करने के लिए सबसे अच्छी जगह कौन सी है? क्या मुझे इसे /WEB-INF/uploads( यहाँ के खिलाफ सलाह दी गई ) या किसी जगह $CATALINA_BASE( यहाँ देखें ) या ... में सहेजना चाहिए ? JavaEE 6 ट्यूटोरियल को उपयोगकर्ता से पथ मिलता है : (wtf :)। एनबी: फ़ाइल किसी भी तरह से डाउनलोड करने योग्य नहीं होनी चाहिए।

  • क्या मुझे यहां विस्तृत रूप में एक कॉन्फ़िगर पैरामीटर सेट करना चाहिए ? मैं कुछ कोड की सराहना करता हूं (मैं इसे एक सापेक्ष पथ देना चाहता हूं - इसलिए यह कम से कम टॉमकैट पोर्टेबल है) - Part.write()आशाजनक दिखता है - लेकिन स्पष्ट रूप से एक पूर्ण पथ की आवश्यकता है

  • मैं एक डेटाबेस / JCR रिपॉजिटरी बनाम इस दृष्टिकोण के नुकसान के एक प्रदर्शनी में दिलचस्पी होगी

दुर्भाग्य से FileServlet @BalusC द्वारा, फ़ाइलें डाउनलोड करने पर केंद्रित है, जबकि उनके जवाब अपलोडिंग फाइलों पर जहां फ़ाइल को बचाने के लिए पर भाग को छोड़ देता है।

डीबी या जेसीआर कार्यान्वयन (जैसे जैकबबिट ) का उपयोग करने के लिए आसानी से परिवर्तनीय समाधान बेहतर होगा।


मेरे अंतिम तरीके को करने के लिए नीचे उत्तर देखें
Mr_and_Mrs_D

जवाबों:


165

आईडीई के प्रोजेक्ट फोल्डर उर्फ ​​सर्वर के फोल्डर के अलावा एक सुलभ स्थान पर कहीं भी स्टोर करें , अपलोड की गई छवि के उत्तर में उल्लिखित कारणों के लिए केवल पृष्ठ को ताज़ा करने के बाद उपलब्ध है :

  1. आईडीई के प्रोजेक्ट फ़ोल्डर में परिवर्तन तुरंत सर्वर के कार्य फ़ोल्डर में परिलक्षित नहीं होता है। IDE में एक तरह का बैकग्राउंड जॉब होता है जो इस बात का ध्यान रखता है कि सर्वर का वर्क फोल्डर अंतिम अपडेट के साथ सिंक हो जाए (यह IDE शब्दों में "प्रकाशन" कहलाता है)। यह आपके द्वारा देखी जा रही समस्या का मुख्य कारण है।

  2. वास्तविक विश्व कोड में ऐसी परिस्थितियां होती हैं जहां वेबैप के फ़ोल्डर में अपलोड की गई फ़ाइलों को संग्रहीत करना बिल्कुल भी काम नहीं करेगा। कुछ सर्वर (या तो डिफ़ॉल्ट रूप से या कॉन्फ़िगरेशन द्वारा) तैनात WAR फ़ाइल को स्थानीय डिस्क फ़ाइल सिस्टम में विस्तारित नहीं करते हैं, लेकिन इसके बजाय पूरी तरह से मेमोरी में। आप मूल रूप से तैनात WAR फ़ाइल को एडिट किए बिना और इसे रीडेपॉल्ड करके मेमोरी में नई फाइलें नहीं बना सकते।

  3. यहां तक ​​कि जब सर्वर तैनात WAR फ़ाइल को स्थानीय डिस्क फ़ाइल सिस्टम में विस्तारित करता है, तो सभी नई बनाई गई फ़ाइलें एक redeploy पर खो जाएगी या यहां तक ​​कि एक सरल पुनरारंभ, बस इसलिए कि उन नई फाइलें मूल WAR फ़ाइल का हिस्सा नहीं हैं।

यह वास्तव में मेरे या किसी और के लिए मायने नहीं रखता है, जहां स्थानीय डिस्क फ़ाइल सिस्टम पर यह सहेजा जाएगा, जब तक आप कभी भी विधि का उपयोग नहीं करते हैंgetRealPath() । उस विधि का उपयोग करना किसी भी मामले में खतरनाक है।

भंडारण स्थान का मार्ग बदले में कई तरीकों से निश्चित किया जा सकता है। आपको यह सब अपने आप से करना होगा । शायद यह वह जगह है जहां आपका भ्रम पैदा होता है क्योंकि आप किसी तरह उम्मीद करते हैं कि सर्वर स्वचालित रूप से सभी करता है। कृपया ध्यान दें कि अंतिम अपलोड गंतव्य निर्दिष्ट नहीं@MultipartConfig(location) करता है, लेकिन केस फ़ाइल आकार के लिए अस्थायी भंडारण स्थान मेमोरी स्टोरेज सीमा से अधिक है।

तो, अंतिम संग्रहण स्थान का रास्ता निम्नलिखित में से किसी एक तरीके से निश्चित किया जा सकता है:

  • हार्ड कोडित:

      File uploads = new File("/path/to/uploads");
  • के माध्यम से पर्यावरण चर SET UPLOAD_LOCATION=/path/to/uploads:

      File uploads = new File(System.getenv("UPLOAD_LOCATION"));
  • के माध्यम से सर्वर स्टार्टअप के दौरान वीएम तर्क -Dupload.location="/path/to/uploads":

      File uploads = new File(System.getProperty("upload.location"));
  • *.propertiesफ़ाइल प्रविष्टि इस प्रकार है upload.location=/path/to/uploads:

      File uploads = new File(properties.getProperty("upload.location"));
  • web.xml <context-param>नाम upload.locationऔर मूल्य के साथ /path/to/uploads:

      File uploads = new File(getServletContext().getInitParameter("upload.location"));
  • यदि कोई हो, तो सर्वर प्रदान किए गए स्थान का उपयोग करें, जैसे JBoss AS / WildFly :

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

किसी भी तरह, आप आसानी से संदर्भ और फ़ाइल को निम्नानुसार सहेज सकते हैं:

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

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

या, जब आप उपयोगकर्ताओं को संयोग से एक ही नाम के साथ मौजूदा फ़ाइलों को अधिलेखित करने से रोकने के लिए एक अद्वितीय फ़ाइल नाम को स्वचालित करना चाहते हैं:

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

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

partजेएसपी / सर्वलेट में कैसे प्राप्त किया जाता है, इसका उत्तर जेएसपी / सर्वलेट का उपयोग करके सर्वर पर फाइलें कैसे अपलोड करें? और partजेएसएफ में कैसे प्राप्त किया जाता है इसका जवाब जेएसएफ 2.2 <h: inputFile> का उपयोग करके फ़ाइल को कैसे अपलोड किया जाए? सहेजी गई फ़ाइल कहाँ है?

नोट: इसका उपयोग करें Part#write()क्योंकि यह परिभाषित अस्थायी भंडारण स्थान के सापेक्ष पथ की व्याख्या करता है @MultipartConfig(location)

यह सभी देखें:


@MultipartConfig(location)निर्दिष्ट अस्थायी भंडारण स्थान जब फ़ाइल आकार स्मृति भंडारण के लिए सीमा से अधिक है जो सर्वर का उपयोग करना चाहिए, नहीं स्थायी भंडारण स्थान है जहाँ आप अंततः यह चाहते हैं संग्रहीत करने के लिए। यह मान java.io.tmpdirसिस्टम गुण द्वारा पहचाने गए पथ पर डिफॉल्ट करता है । एक असफल JSF प्रयास के इस संबंधित उत्तर को भी देखें: stackoverflow.com/questions/18478154/…
BalusC

1
धन्यवाद - आशा है कि मैं बेवकूफ नहीं लग रहा हूँ, लेकिन इस उद्धरण से Part.write>> यह एक विशेष कार्यान्वयन का उपयोग करने की अनुमति देता है, उदाहरण के लिए, फ़ाइल का नामकरण, जहाँ संभव हो, अंतर्निहित डेटा के सभी कॉपी करने के बजाय, इस प्रकार कुछ के साथ संयोजन में एक महत्वपूर्ण प्रदर्शन लाभ प्राप्त करना। अज्ञात "कट" (बनाम कॉपी) विधि कहती है कि कुछ अपाचे काम मुझे खुद बाइट्स लिखने की परेशानी से बचाएंगे - और पहले से ही वहां एक फ़ाइल को फिर से बनाना ( यहाँ भी देखें )
Mr_and_Mrs_D

हां, यदि आप पहले से ही सर्वलेट 3.0 पर हैं, तो आप इसका उपयोग कर सकते हैं Part#write()। मैंने इसके साथ उत्तर को अपडेट किया।
बालूसी

पोस्ट को अपडेट रखने के लिए आपका बहुत बहुत धन्यवाद - क्या टॉमकैट के लिए ऐसी कोई संपत्ति है "jboss.server.data.dir"?
Mr_and_Mrs_D

1
नहीं, यह नहीं है।
बालुस

7

मैं स्वीकृत उत्तर के आधार पर इसे करने का अपना अंतिम तरीका पोस्ट करता हूं:

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

कहाँ पे :

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

और /WEB-INF/app.properties:

upload.location=C:/_/

HTH और अगर आपको कोई बग मिलता है तो मुझे बताएं


1
क्या होगा अगर मुझे एक एसओ स्वतंत्र समाधान चाहिए, जो दोनों (जीत / ux) मामले में काम करता है? क्या मुझे अलग-अलग अपलोड करना होगा। आवंटन पथ या कोई अन्य संकेत है?
चिकमोटा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.