वसंत नियंत्रकों से एक फ़ाइल डाउनलोड करना


387

मेरे पास एक आवश्यकता है जहां मुझे वेबसाइट से पीडीएफ डाउनलोड करने की आवश्यकता है। पीडीएफ को कोड के भीतर उत्पन्न करने की आवश्यकता है, जो मुझे लगा कि फ्रीमार्क और कॉक्सट्रा जैसी पीडीएफ पीढ़ी के ढांचे का संयोजन होगा। कोई बेहतर तरीका?

हालांकि, मेरी मुख्य समस्या यह है कि मैं उपयोगकर्ता को स्प्रिंग कंट्रोलर के माध्यम से फ़ाइल डाउनलोड करने की अनुमति कैसे दूं?


2
यह उल्लेखनीय है कि स्प्रिंग फ्रेमवर्क 2011 के बाद से बहुत बदल गया है, इसलिए आप इसे प्रतिक्रियात्मक तरीके से भी कर सकते हैं - यहां एक उदाहरण है
Krzysztof Skrzynecki

जवाबों:


397
@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
public void getFile(
    @PathVariable("file_name") String fileName, 
    HttpServletResponse response) {
    try {
      // get your file as InputStream
      InputStream is = ...;
      // copy it to response's OutputStream
      org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
      response.flushBuffer();
    } catch (IOException ex) {
      log.info("Error writing file to output stream. Filename was '{}'", fileName, ex);
      throw new RuntimeException("IOError writing file to output stream");
    }

}

सामान्यतया, जब आपके पास होता है response.getOutputStream(), तो आप वहां कुछ भी लिख सकते हैं। आप अपने जनरेटर में पीडीएफ उत्पन्न करने के स्थान के रूप में इस आउटपुट स्ट्रीम को पारित कर सकते हैं। इसके अलावा, यदि आप जानते हैं कि आप किस फ़ाइल प्रकार को भेज रहे हैं, तो आप सेट कर सकते हैं

response.setContentType("application/pdf");

4
यह बहुत ज्यादा है जो मैं कहने वाला था, लेकिन आपको संभवतः प्रतिक्रिया प्रकार हेडर को फ़ाइल के लिए उपयुक्त कुछ पर सेट करना चाहिए।
गैरीफ

2
हां, बस पोस्ट को संपादित किया। मेरे पास विभिन्न प्रकार की फ़ाइल उत्पन्न हुई थी, इसलिए मैंने इसके एक्सटेंशन द्वारा फ़ाइल के सामग्री प्रकार को निर्धारित करने के लिए इसे ब्राउज़र पर छोड़ दिया।
Infeligo

फ्लश बफ़र को भूल गए, आपकी पोस्ट के लिए धन्यवाद, मैंने देखा कि मेरा काम क्यों नहीं था :-)
Jan Vladimir Mostert

35
IOUtilsस्प्रिंग के बजाय अपाचे का उपयोग करने का कोई विशेष कारण FileCopyUtils?
पॉवरलॉर्ड

3
यहाँ एक बेहतर समाधान है: stackoverflow.com/questions/16652760/…
Dmytro Plekhotkin

290

मैं इसे स्प्रिंग में सपोर्ट के साथ निर्मित करके इसे स्ट्रीम करने में सक्षम था। यह सामग्री-लंबाई और सामग्री-प्रकार सेट करेगा यदि यह माइम-प्रकार निर्धारित कर सकता है

@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
    return new FileSystemResource(myService.getFileFor(fileName)); 
}

10
यह काम। लेकिन फ़ाइल (.csv फ़ाइल) ब्राउज़र में प्रदर्शित होती है और डाउनलोड नहीं होती है - मैं ब्राउज़र को डाउनलोड करने के लिए कैसे मजबूर कर सकता हूं?
चज़ब्रगला

41
आप डाउनलोड करने के लिए मजबूर करने के लिए @RequestMapping पर - MediaType.APPLICATION_OCTET_STREAM_VALUE = मीडिया जोड़ सकते हैं
David Kago

8
इसके अलावा आप <bean class = " org.springframework.http.converter.ResourceHttpMessageConverter " /> को संदेश संदेश सूची (<mvc: एनोटेशन-चालित - <mvc: संदेश-कन्वर्टर्स>)
Sllouyssgort

4
क्या Content-Dispositionहेडर को इस तरह से सेट करने का कोई तरीका है?
राल्फ

8
मुझे उसके लिए कोई आवश्यकता नहीं थी, लेकिन मुझे लगता है कि आप विधि के पैरामीटर के रूप में HttpResponse जोड़ सकते हैं, और फिर "response.setHeader (" सामग्री-विवाद "," अनुलग्नक; फ़ाइल नाम = somefile.pdf ");"
स्कॉट कार्लसन

82

आपको फ़ाइल को सीधे प्रतिक्रिया पर लिखने में सक्षम होना चाहिए। कुछ इस तरह

response.setContentType("application/pdf");      
response.setHeader("Content-Disposition", "attachment; filename=\"somefile.pdf\""); 

और फिर फ़ाइल को बाइनरी स्ट्रीम के रूप में लिखें response.getOutputStream()response.flush()अंत में करना याद रखें और इसे करना चाहिए।


8
सामग्री प्रकार इस तरह सेट करने के लिए 'स्प्रिंग' तरीका नहीं है? @RequestMapping(value = "/foo/bar", produces = "application/pdf")
ब्लैक

4
यदि आपके एप्लिकेशन में अलग-अलग फ़ाइल प्रकार डाउनलोड हैं, तो @ फ़्रैंकिस क्या है? लॉबस्टर 1234 का जवाब आपको सामग्री के स्वभाव को गतिशील रूप से सेट करने में सक्षम बनाता है।
रोज़

2
यह सच है @ सही, लेकिन मेरा मानना ​​है कि प्रति प्रारूप अलग-अलग बिंदुओं को परिभाषित करने के लिए बेहतर अभ्यास होगा
ब्लैक

3
मुझे लगता है कि नहीं, क्योंकि यह स्केलेबल नहीं है। वर्तमान में हम एक दर्जन प्रकार के संसाधनों का समर्थन कर रहे हैं। हम इस बात पर आधारित अधिक फ़ाइल प्रकारों का समर्थन कर सकते हैं कि उपयोगकर्ता उस स्थिति में अपलोड करना चाहते हैं जो हम इतने अंत बिंदुओं के साथ अनिवार्य रूप से एक ही काम कर सकते हैं। IMHO में केवल एक डाउनलोड अंत बिंदु होना चाहिए और यह फ़ाइल प्रकारों की भीड़ को संभालता है। @Francis
गुलाब

3
यह बिल्कुल "स्केलेबल" है, लेकिन हम असहमत हो सकते हैं कि क्या यह सबसे अच्छा अभ्यास है
ब्लैक

74

स्प्रिंग 3.0 के साथ आप HttpEntityरिटर्न ऑब्जेक्ट का उपयोग कर सकते हैं । यदि आप इसका उपयोग करते हैं, तो आपके नियंत्रक को एक HttpServletResponseवस्तु की आवश्यकता नहीं है , और इसलिए यह परीक्षण करना आसान है। इसके अलावा, यह उत्तर Infeligo के सापेक्ष समान है

यदि आपके पीडीएफ फ्रेमवर्क का रिटर्न मूल्य एक बाइट सरणी है (अन्य रिटर्न मानों के लिए मेरे उत्तर का दूसरा भाग पढ़ें) :

@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
                 @PathVariable("fileName") String fileName) throws IOException {

    byte[] documentBody = this.pdfFramework.createPdf(filename);

    HttpHeaders header = new HttpHeaders();
    header.setContentType(MediaType.APPLICATION_PDF);
    header.set(HttpHeaders.CONTENT_DISPOSITION,
                   "attachment; filename=" + fileName.replace(" ", "_"));
    header.setContentLength(documentBody.length);

    return new HttpEntity<byte[]>(documentBody, header);
}

यदि आपके पीडीएफ फ्रेमवर्क का रिटर्न प्रकार documentBbodyपहले से ही बाइट सरणी नहीं है (और यह भी नहीं ByteArrayInputStream) तो यह समझदार नहीं होगा कि इसे पहले बाइट सरणी बनाया जाए। इसके बजाय इसका उपयोग करना बेहतर है:

उदाहरण के साथ FileSystemResource:

@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
                 @PathVariable("fileName") String fileName) throws IOException {

    File document = this.pdfFramework.createPdf(filename);

    HttpHeaders header = new HttpHeaders();
    header.setContentType(MediaType.APPLICATION_PDF);
    header.set(HttpHeaders.CONTENT_DISPOSITION,
                   "attachment; filename=" + fileName.replace(" ", "_"));
    header.setContentLength(document.length());

    return new HttpEntity<byte[]>(new FileSystemResource(document),
                                  header);
}

11
-1 यह पूरी तरह से मेमोरी में पूरी फाइल को लोड कर देगा, आसानी से OutOfMemoryErrors को कैस कर सकता है।
फैसल फिरोज

1
@FaisalFeroz: हाँ यह सही है, लेकिन फ़ाइल दस्तावेज़ वैसे भी मेमोरी में बनाया गया है (प्रश्न देखें: "पीडीएफ को कोड के भीतर उत्पन्न होने की आवश्यकता है")। वैसे भी - आपका क्या उपाय है जो इस समस्या को दूर करता है?
राल्फ

1
आप ResponseEntity का भी उपयोग कर सकते हैं जो HttpEntity का एक सुपर है जो आपको प्रतिक्रिया http स्थिति कोड को निर्दिष्ट करने की अनुमति देता है। उदाहरण:return new ResponseEntity<byte[]>(documentBody, headers, HttpStatus.CREATED)
अमृत ​​मोस्तफा

@Arr Mostafa: दूसरी ओर 201 ResponseEntityका एक उपवर्ग है HttpEntity(लेकिन मैं इसे प्राप्त करता हूं) सृजन किया गया है वह नहीं है जब मैं डेटा का केवल एक दृश्य वापस करता हूं। ( 2013 के लिए w3.org/Protocols/rfc2616/rfc2616-sec10.html देखें )
राल्फ

1
क्या कोई कारण है कि आप फ़ाइल नाम में अंडरस्कोर के साथ व्हॉट्सएप की जगह ले रहे हैं? वास्तविक नाम भेजने के लिए आप इसे कोट में लपेट सकते हैं।
अलेक्जेंड्रू सेवरिन

63

अगर तुम:

  • byte[]प्रतिक्रिया भेजने से पहले पूरी फ़ाइल को लोड नहीं करना चाहते ;
  • / के माध्यम से इसे भेजने / डाउनलोड करने की आवश्यकता है InputStream;
  • भेजी गई माइम प्रकार और फ़ाइल नाम का पूर्ण नियंत्रण चाहते हैं;
  • @ControllerAdviceआप (या नहीं) के लिए अन्य उठा रहा है।

नीचे दिए गए कोड की आपको आवश्यकता है:

@RequestMapping(value = "/stuff/{stuffId}", method = RequestMethod.GET)
public ResponseEntity<FileSystemResource> downloadStuff(@PathVariable int stuffId)
                                                                      throws IOException {
    String fullPath = stuffService.figureOutFileNameFor(stuffId);
    File file = new File(fullPath);
    long fileLength = file.length(); // this is ok, but see note below

    HttpHeaders respHeaders = new HttpHeaders();
    respHeaders.setContentType("application/pdf");
    respHeaders.setContentLength(fileLength);
    respHeaders.setContentDispositionFormData("attachment", "fileNameIwant.pdf");

    return new ResponseEntity<FileSystemResource>(
        new FileSystemResource(file), respHeaders, HttpStatus.OK
    );
}

फ़ाइल लंबाई भाग के बारे में : File#length()सामान्य मामले में पर्याप्त होना चाहिए, लेकिन मुझे लगा कि मैं यह अवलोकन करूंगा क्योंकि यह धीमा हो सकता है , जिस स्थिति में आपको इसे पहले संग्रहीत करना चाहिए (जैसे डीबी में)। मामलों में यह धीमा हो सकता है: यदि फ़ाइल बड़ी है, तो विशेष रूप से यदि फ़ाइल किसी दूरस्थ प्रणाली में है या कुछ और जैसे कि - एक डेटाबेस, शायद।



InputStreamResource

यदि आपका संसाधन कोई फ़ाइल नहीं है, जैसे आप DB से डेटा उठाते हैं, तो आपको उपयोग करना चाहिए InputStreamResource। उदाहरण:

    InputStreamResource isr = new InputStreamResource(new FileInputStream(file));
    return new ResponseEntity<InputStreamResource>(isr, respHeaders, HttpStatus.OK);

आप FileSystemResource वर्ग के उपयोग के लिए सलाह नहीं देते हैं?
स्टीफन

वास्तव में, मेरा मानना ​​है कि FileSystemResourceवहाँ का उपयोग करना ठीक है । यदि आपका संसाधन एक फ़ाइल है तो यह उचित भी है । इस नमूने में, FileSystemResourceजहां इस्तेमाल किया जा सकता InputStreamResourceहै।
acccjunior

फ़ाइल लंबाई गणना भाग के बारे में: यदि आप चिंतित हैं, तो मत बनो। File#length()सामान्य मामले में पर्याप्त होना चाहिए। मैंने अभी इसका उल्लेख किया है क्योंकि यह धीमा हो सकता है , विशेष रूप से अगर फ़ाइल एक दूरस्थ प्रणाली में है या कुछ और अधिक विस्तृत है जैसे - एक डेटाबेस, शायद? लेकिन केवल यह चिंता करें कि क्या यह एक समस्या बन जाती है (या यदि आपके पास कठिन सबूत हैं तो यह एक हो जाएगा), पहले नहीं। मुख्य बिंदु यह है: आप फ़ाइल को स्ट्रीम करने का एक प्रयास कर रहे हैं, यदि आपको पहले यह सब पहले से लोड करना है, तो स्ट्रीमिंग को कोई फर्क नहीं पड़ता है, एह?
acccjunior

उपरोक्त कोड मेरे लिए काम क्यों नहीं करता है? यह 0 बाइट्स फ़ाइल डाउनलोड करता है। मैंने जाँच की और सुनिश्चित किया कि बाइटएयर्रे और रिसोर्समेसेज कन्वर्टर्स वहाँ हों। क्या मैं कुछ भूल रहा हूँ ?
कोडिंग_डियोट

आप बाइटएयर्रे और रिसोर्समैसेज कन्वर्टर्स के बारे में क्यों चिंता कर रहे हैं?
acccjunior

20

यह कोड jsp पर एक लिंक पर क्लिक करके स्प्रिंग कंट्रोलर से स्वचालित रूप से एक फ़ाइल डाउनलोड करने के लिए ठीक काम कर रहा है।

@RequestMapping(value="/downloadLogFile")
public void getLogFile(HttpSession session,HttpServletResponse response) throws Exception {
    try {
        String filePathToBeServed = //complete file name with path;
        File fileToDownload = new File(filePathToBeServed);
        InputStream inputStream = new FileInputStream(fileToDownload);
        response.setContentType("application/force-download");
        response.setHeader("Content-Disposition", "attachment; filename="+fileName+".txt"); 
        IOUtils.copy(inputStream, response.getOutputStream());
        response.flushBuffer();
        inputStream.close();
    } catch (Exception e){
        LOGGER.debug("Request could not be completed at this moment. Please try again.");
        e.printStackTrace();
    }

}

14

नीचे दिए गए कोड ने मुझे एक टेक्स्ट फ़ाइल बनाने और डाउनलोड करने के लिए काम किया।

@RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity<byte[]> getDownloadData() throws Exception {

    String regData = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
    byte[] output = regData.getBytes();

    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.set("charset", "utf-8");
    responseHeaders.setContentType(MediaType.valueOf("text/html"));
    responseHeaders.setContentLength(output.length);
    responseHeaders.set("Content-disposition", "attachment; filename=filename.txt");

    return new ResponseEntity<byte[]>(output, responseHeaders, HttpStatus.OK);
}

5

मैं जल्दी से क्या सोच सकता हूं, पीडीएफ जेनरेट करें और इसे वेबैप / डाउनलोड / <RANDOM-FILENAME> .pdf में स्टोर करें और HttpServletRequest का उपयोग करके इस फाइल को भेजें।

request.getRequestDispatcher("/downloads/<RANDOM-FILENAME>.pdf").forward(request, response);

या यदि आप अपना विचार रिज़ॉल्वर को कॉन्फ़िगर कर सकते हैं जैसे,

  <bean id="pdfViewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass"
              value="org.springframework.web.servlet.view.JstlView" />
    <property name="order" value=”2″/>
    <property name="prefix" value="/downloads/" />
    <property name="suffix" value=".pdf" />
  </bean>

तो बस वापस आ जाओ

return "RANDOM-FILENAME";

1
अगर मुझे दो दृश्य रिज़ॉल्वर चाहिए, तो मैं रिज़ॉल्वर का नाम कैसे वापस कर सकता हूं या इसे नियंत्रक में चुन सकता हूं ??
अजराफती

3

निम्नलिखित समाधान मेरे लिए काम करते हैं

    @RequestMapping(value="/download")
    public void getLogFile(HttpSession session,HttpServletResponse response) throws Exception {
        try {

            String fileName="archivo demo.pdf";
            String filePathToBeServed = "C:\\software\\Tomcat 7.0\\tmpFiles\\";
            File fileToDownload = new File(filePathToBeServed+fileName);

            InputStream inputStream = new FileInputStream(fileToDownload);
            response.setContentType("application/force-download");
            response.setHeader("Content-Disposition", "attachment; filename="+fileName); 
            IOUtils.copy(inputStream, response.getOutputStream());
            response.flushBuffer();
            inputStream.close();
        } catch (Exception exception){
            System.out.println(exception.getMessage());
        }

    }

2

नीचे जैसा कुछ

@RequestMapping(value = "/download", method = RequestMethod.GET)
public void getFile(HttpServletResponse response) {
    try {
        DefaultResourceLoader loader = new DefaultResourceLoader();
        InputStream is = loader.getResource("classpath:META-INF/resources/Accepted.pdf").getInputStream();
        IOUtils.copy(is, response.getOutputStream());
        response.setHeader("Content-Disposition", "attachment; filename=Accepted.pdf");
        response.flushBuffer();
    } catch (IOException ex) {
        throw new RuntimeException("IOError writing file to output stream");
    }
}

आप पीडीएफ को प्रदर्शित कर सकते हैं या इसे यहां से डाउनलोड कर सकते हैं


1

अगर यह किसी की मदद करता है। आप वही कर सकते हैं, जो Infeligo ने स्वीकार किया है, लेकिन एक अतिरिक्त डाउनलोड के लिए कोड में यह अतिरिक्त बिट डाल दिया है।

response.setContentType("application/force-download");

0

यह एक उपयोगी उत्तर हो सकता है।

क्या दृश्यपटल में पीडीएफ प्रारूप के रूप में डेटा निर्यात करना ठीक है?

इसे संलग्न करते हुए, अनुलग्नक (डिफ़ॉल्ट) के रूप में सामग्री-स्वभाव को जोड़ने से फ़ाइल डाउनलोड हो जाएगी। यदि आप इसे देखना चाहते हैं, तो आपको इसे इनलाइन पर सेट करना होगा।


0

मेरे मामले में मैं मांग पर कुछ फ़ाइल उत्पन्न कर रहा हूं, इसलिए url भी जेनरेट करना होगा।

मेरे लिए कुछ इस तरह काम करता है:

@RequestMapping(value = "/files/{filename:.+}", method = RequestMethod.GET, produces = "text/csv")
@ResponseBody
public FileSystemResource getFile(@PathVariable String filename) {
    String path = dataProvider.getFullPath(filename);
    return new FileSystemResource(new File(path));
}

बहुत महत्वपूर्ण माइम प्रकार है producesऔर यह भी कि, फ़ाइल का नाम लिंक का एक हिस्सा है, इसलिए आपको इसका उपयोग करना होगा @PathVariable

HTML कोड इस तरह दिखता है:

<a th:href="@{|/dbreport/files/${file_name}|}">Download</a>

कहाँ ${file_name}नियंत्रक में Thymeleaf द्वारा उत्पन्न की जाती है तथा अर्थात्: result_20200225.csv, ताकि पूरे यूआरएल behing लिंक है: example.com/aplication/dbreport/files/result_20200225.csv

लिंक पर क्लिक करने के बाद ब्राउज़र मुझसे पूछता है कि फाइल के साथ क्या करना है - सेव या ओपन।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.