বসন্ত নিয়ন্ত্রণকারীদের থেকে একটি ফাইল ডাউনলোড করা


387

আমার একটি প্রয়োজন আছে যেখানে আমার ওয়েবসাইট থেকে পিডিএফ ডাউনলোড করতে হবে। পিডিএফ কোডের মধ্যে তৈরি করা দরকার যা আমি ভেবেছিলাম ফ্রিমার্কারের এবং আইটেক্সটের মতো পিডিএফ প্রজন্মের কাঠামোর সংমিশ্রণ হবে। আর কোন ভাল উপায়?

তবে, আমার মূল সমস্যাটি হল আমি কীভাবে ব্যবহারকারীকে একটি স্প্রিং কন্ট্রোলারের মাধ্যমে কোনও ফাইল ডাউনলোড করার অনুমতি দেব?


2
এটি উল্লেখযোগ্য যে স্প্রিং ফ্রেমওয়ার্কটি ২০১১ সাল থেকে অনেকটা পরিবর্তিত হয়েছে, সুতরাং আপনি এটি একটি প্রতিক্রিয়াশীল উপায়েও করতে পারেন - এখানে একটি উদাহরণ রয়েছে
ক্রিজিসটফ স্ক্রাজিনেককি

উত্তর:


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
হ্যাঁ, স্রেফ পোস্ট সম্পাদনা করেছেন। আমার বিভিন্ন ধরণের ফাইল উত্পন্ন হয়েছিল, তাই আমি ফাইলের সামগ্রীর ধরণটি এর এক্সটেনশান দ্বারা নির্ধারণ করতে ব্রাউজারে রেখে দিয়েছি।
ইনফিলিগো

ফ্লাশবাফার ভুলে গেছেন, আপনার পোস্টের জন্য ধন্যবাদ, আমি দেখলাম কেন আমার কাজ করছে না :-)
জান ভ্লাদিমির মোস্টার্ট

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 ফাইল) ব্রাউজারে প্রদর্শিত হয় এবং ডাউনলোড হয় না - আমি কীভাবে ব্রাউজারটি ডাউনলোড করতে বাধ্য করতে পারি?
chzbrgla

41
আপনাকে জোর করে ডাউনলোড @RequestMapping যোগ করতে পারেন উত্পাদন করে = MediaType.APPLICATION_OCTET_STREAM_VALUE
ডেভিড Kago

8
এছাড়াও আপনার <বিয়ান ক্লাস = "org.springframework.http.converter.ResourceHttpMessageConverter" /> বার্তা রূপান্তরকারীদের তালিকায় যোগ করা উচিত (<এমভিসি: টিকা-চালিত> <এমভিসি: বার্তা-রূপান্তরকারী))
স্লোইয়সগোর্ট

4
এইভাবে Content-Dispositionশিরোলেখ সেট করার কোনও উপায় আছে কি ?
রাল্ফ

8
আমার এটির কোনও প্রয়োজন ছিল না, তবে আমি মনে করি আপনি এই পদ্ধতিতে HTPpResponseটিকে প্যারামিটার হিসাবে যুক্ত করতে পারেন এবং তারপরে "प्रतिक्रिया.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
আমি অনুমান করি না, কারণ এটি স্কেলযোগ্য নয়। আমরা বর্তমানে এক ডজন ধরণের সংস্থানকে সমর্থন করছি। ব্যবহারকারীরা যে ক্ষেত্রে আপলোড করতে চান তার উপর ভিত্তি করে আমরা আরও ফাইল প্রকারের সমর্থন করতে পারি আমরা মূলত একই জিনিসটি করার ফলে অনেকগুলি শেষ পয়েন্ট দিয়ে শেষ করতে পারি। আইএমএইচও-তে কেবল একটি ডাউনলোড শেষ পয়েন্ট থাকতে হবে এবং এটি প্রচুর পরিমাণে ফাইল ধরণের পরিচালনা করে। @ ফ্রেঞ্চিস
রোজ

3
এটি একেবারে "স্কেলেবল", তবে এটি সর্বোত্তম অনুশীলন কিনা তা আমরা একমত করতে সম্মত হতে পারি
কালো

74

স্প্রিং 3.0 এর সাথে আপনি HttpEntityরিটার্ন অবজেক্টটি ব্যবহার করতে পারেন। আপনি যদি এটি ব্যবহার করেন, তবে আপনার নিয়ামকের কোনও কোনও HttpServletResponseজিনিসের প্রয়োজন নেই এবং তাই এটি পরীক্ষা করা সহজ। এটি বাদে, এই উত্তরটি ইনফিলিগোর সাথে সমান

যদি আপনার পিডিএফ ফ্রেমওয়ার্কের রিটার্ন মান একটি বাইট অ্যারে হয় (অন্যান্য রিটার্ন মানগুলির জন্য আমার উত্তরের দ্বিতীয় অংশটি পড়ুন) :

@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 এটি অনিয়মিতভাবে মেমরিতে পুরো ফাইলটি লোড করবে সহজেই আউটআফমিউরিইরিজগুলি সনাক্ত করতে পারে।
ফয়সাল ফিরোজ

1
@ ফয়সালফেরোজ: হ্যাঁ এটি ঠিক, তবে ফাইল নথিটি যাইহোক মেমরিতে তৈরি করা হয়েছে (প্রশ্নটি দেখুন: "কোডের মধ্যে পিডিএফ তৈরি করা দরকার")। যাইহোক - আপনার সমাধান কী যা এই সমস্যাটি কাটিয়ে উঠেছে?
রাল্ফ

1
আপনি রেসপন্স এন্টিটিও ব্যবহার করতে পারেন যা HTTPEntity এর একটি সুপার যা আপনাকে প্রতিক্রিয়া HTTP স্থিতি কোড নির্দিষ্ট করতে দেয়। উদাহরণ:return new ResponseEntity<byte[]>(documentBody, headers, HttpStatus.CREATED)
আমর মোস্তফা

@ আমর মোস্তফা: অন্যদিকে ResponseEntityএকটি সাবক্লাস HttpEntity(তবে আমি এটি পেয়েছি) 201 আমি তৈরি করা ডেটাতে কেবল একটি দৃষ্টিভঙ্গি ফিরিয়ে দিলে আমি ব্যবহার করব না। ( w3.org/Protocols/rfc2616/rfc2616-sec10.html ২০১৮ 201 এর জন্য তৈরি করা হয়েছে)
রাল্ফ

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

যদি আপনার উত্স কোনও ফাইল না হয়, যেমন আপনি ডিবি থেকে ডেটা তুলে নেন, আপনার ব্যবহার করা উচিত InputStreamResource। উদাহরণ:

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

আপনি ফাইলসিস্টেম রিসোর্স শ্রেণীর ব্যবহারের জন্য পরামর্শ দিচ্ছেন না?
স্টিফেন

আসলে, আমি বিশ্বাস করি যে এটি ব্যবহার করা ঠিক FileSystemResourceআছে। এমনকি যদি আপনার সংস্থানটি কোনও ফাইল হয় তবে এটির পরামর্শ দেওয়া হয় । এই নমুনায়, FileSystemResourceব্যবহার করা যেতে পারে যেখানে InputStreamResource
acdcjunior

ফাইল দৈর্ঘ্যের গণনার অংশ সম্পর্কে: আপনি যদি উদ্বিগ্ন থাকেন তবে তা হবেন না। File#length()সাধারণ ক্ষেত্রে যথেষ্ট ভাল হওয়া উচিত। আমি কেবল এটি উল্লেখ করেছি কারণ এটি ধীর হতে পারে , বিশেষত যদি ফাইলটি কোনও দূরবর্তী সিস্টেমে থাকে বা এর মতো আরও কিছু ব্যাখ্যা করা হয় - একটি ডাটাবেস, সম্ভবত ?. তবে কেবল চিন্তার কারণ যদি এটি সমস্যা হয়ে যায় (বা যদি আপনার কাছে শক্ত প্রমাণ থাকে তবে এটি এক হয়ে যাবে), আগে নয়। মূল কথাটি হ'ল: আপনি ফাইলটি স্ট্রিম করার চেষ্টা করছেন, যদি আপনাকে এর আগে সমস্ত কিছু প্রিললোড করতে হয় তবে স্ট্রিমিংটি কোনও তফাত ছাড়াই শেষ হয়, তাই না?
acdcjunior

উপরের কোডটি আমার পক্ষে কেন কাজ করছে না? এটি 0 বাইট ফাইল ডাউনলোড করে। আমি চেক করে নিশ্চিত করেছি যে বাইটআরারি এবং রিসোর্সমেসেজ কনভার্টর রয়েছে। আমি কিছু অনুপস্থিত করছি ?
কোডিং_আইডিয়ট

আপনি বাইটআরারি এবং রিসোর্সমেসেজ রূপান্তরকারীদের সম্পর্কে কেন উদ্বিগ্ন?
acdcjunior

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 এ সঞ্চয় করুন এবং এইচটিটিপি সার্ভলেটআরকোয়েস্ট ব্যবহার করে এই ফাইলে একটি ফরোয়ার্ড প্রেরণ করুন

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

এটি যদি কাউকে সাহায্য করে। ইনফিলিগের গৃহীত উত্তর যা বলেছিল তা আপনি করতে পারেন তবে জোরপূর্বক ডাউনলোডের জন্য কোডটিতে এই অতিরিক্ত বিটটি রেখে দিন।

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

0

এটি একটি দরকারী উত্তর হতে পারে।

সামনের অংশে পিডিএফ ফর্ম্যাট হিসাবে ডেটা রফতানি করা ঠিক আছে কি?

এটি প্রসারিত করার সাথে সংযুক্তি (ডিফল্ট) হিসাবে সামগ্রী-স্বভাব যুক্ত করা ফাইলটি ডাউনলোড করবে। আপনি যদি এটি দেখতে চান তবে আপনার এটি ইনলাইনে সেট করতে হবে।


0

আমার ক্ষেত্রে আমি চাহিদা অনুযায়ী কিছু ফাইল উত্পন্ন করছি, তাই urlও উত্পন্ন করতে হবে be

আমার জন্য এমন কিছু কাজ করে:

@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

এইচটিএমএল কোডটি দেখতে এমন দেখাচ্ছে:

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

যেখানে ${file_name}নিয়ামক হিসাবে থাইমালিফ উত্পাদিত হয় এবং যেমন: ফলাফল_20200225.csv, যাতে সম্পূর্ণ ইউআরএল লিঙ্কটি হ'ল:example.com/aplication/dbreport/files/result_20200225.csv

লিঙ্ক ব্রাউজারে ক্লিক করার পরে আমাকে ফাইল - সেভ বা খুলুন দিয়ে কী করতে হবে তা জিজ্ঞাসা করে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.