অ্যাঙ্গুলার 2 বা তার চেয়ে বড় এর সাথে আমি কীভাবে কোনও ফাইল ডাউনলোড করব


182

আমার কাছে একটি ওয়েবএপি / এমভিসি অ্যাপ্লিকেশন রয়েছে যার জন্য আমি একটি কৌণিক 2 ক্লায়েন্ট তৈরি করছি (এমভিসি প্রতিস্থাপন করতে) to অ্যাঙ্গুলার কীভাবে কোনও ফাইল সংরক্ষণ করে তা বুঝতে আমার কিছুটা সমস্যা হচ্ছে।

অনুরোধটি ঠিক আছে (এমভিসির সাথে ঠিকঠাক কাজ করে, এবং আমরা প্রাপ্ত ডেটা লগ করতে পারি) তবে ডাউনলোড করা ডেটা কীভাবে সংরক্ষণ করতে হয় তা আমি বুঝতে পারি না (আমি বেশিরভাগই এই পোস্টের মতো একই যুক্তি অনুসরণ করছি )। আমি নিশ্চিত যে এটি নির্বোধের মতো সহজ, তবে এখন পর্যন্ত আমি কেবল এটি উপলব্ধি করছি না।

উপাদান ফাংশনের কোডটি নীচে। আমি বিভিন্ন বিকল্প চেষ্টা করেছি, ফোঁটা উপায় হিসেবে এ পর্যন্ত আমি বুঝতে হিসাবে যেতে উপায় হওয়া উচিত, কিন্তু কোন ফাংশন createObjectURLমধ্যে URL। আমি URLউইন্ডোতে সংজ্ঞাও পাই না , তবে দৃশ্যত এটি বিদ্যমান। আমি যদি FileSaver.jsমডিউলটি ব্যবহার করি তবে আমি একই ত্রুটি পাই। সুতরাং আমি অনুমান করি এটি এমন কিছু যা সম্প্রতি পরিবর্তিত হয়েছে বা এখনও বাস্তবায়িত হয়নি। আমি কীভাবে এ 2-তে ফাইলটি সেভ করতে পারি?

downloadfile(type: string){

    let thefile = {};
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(data => thefile = new Blob([data], { type: "application/octet-stream" }), //console.log(data),
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    let url = window.URL.createObjectURL(thefile);
    window.open(url);
}

সম্পূর্ণতার স্বার্থে, পরিষেবাটি ডেটা আনার জন্য নীচে রয়েছে, তবে এটি কেবলমাত্র অনুরোধটি জারি করে এবং যদি সফল হয় তবে ম্যাপিং ছাড়াই ডেটাতে পাস করা:

downloadfile(runname: string, type: string){
   return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .catch(this.logAndPassOn);
}

আপনি এই পদ্ধতিতে বড় ফাইল ডাউনলোড করতে পারবেন না। আপনি প্রতি ট্যাবে মেমরির সীমাটি আঘাত করবেন। এটি 1-2GB এর মতো কম হতে পারে।
ম্যাথু বি

@MatthewB। আশা করি আপনি কি ভাল ছিল বলতেন।
স্টিভ

বড় ফাইল ডাউনলোডের জন্য আপনাকে একটি নতুন ট্যাব নির্দিষ্ট করতে হবে যেমন একটি <A> ক্লিক সিমুলেট করা হলে লক্ষ্যটিকে "_blank" সমান করতে হবে বা একটি ফর্ম জমা দিতে হবে। আজাক্স-স্টাইল অনুরোধগুলির সাথে বড় ফাইল আকারের সীমাবদ্ধতার কাছাকাছি যাওয়ার কোনও পরিষ্কার উপায় আছে বলে আমি মনে করি না।
ম্যাথু বি।

উত্তর:


181

সমস্যাটি হ'ল পর্যবেক্ষণযোগ্যটি অন্য প্রসঙ্গে চলে, সুতরাং আপনি যখন URL টি তৈরির চেষ্টা করবেন তখন আপনার খালি অবজেক্ট রয়েছে এবং আপনি যে ব্লবটি চান তা নয়।

এটি সমাধানের জন্য বিদ্যমান বিভিন্ন উপায়ের একটি নিম্নরূপ:

this._reportService.getReport().subscribe(data => this.downloadFile(data)),//console.log(data),
                 error => console.log('Error downloading the file.'),
                 () => console.info('OK');

অনুরোধটি প্রস্তুত হয়ে গেলে এটি ফাংশনটিকে "ডাউনলোডফাইলে" কল করবে যা নীচে বর্ণিত:

downloadFile(data: Response) {
  const blob = new Blob([data], { type: 'text/csv' });
  const url= window.URL.createObjectURL(blob);
  window.open(url);
}

ব্লবটি নিখুঁতভাবে তৈরি করা হয়েছে এবং তাই ইউআরএল ভার, যদি নতুন উইন্ডো না খোলেন তবে দয়া করে পরীক্ষা করুন যে আপনি ইতিমধ্যে 'rxjs / Rx' আমদানি করেছেন;

  import 'rxjs/Rx' ;

আমি আশা করি এটা তোমাকে সাহায্য করবে।


9
কী this._reportService.getReport()এবং কী ফিরে আসে?
বুর্জুয়া

3
@Burjua getReport()আয় একটিthis.http.get(PriceConf.download.url)
Ji-রূহুল

6
আমার যে সমস্যাটি হচ্ছে তা হ'ল উইন্ডোটি ফাইলটি ডাউনলোড না করে অবিলম্বে খোলে এবং বন্ধ হয়ে যায়
ব্র্যাডেন ব্রাউন

7
আমরা এখানে ফাইলের নামটি কীভাবে সেট করতে পারি? ডিফল্টরূপে এটি নাম হিসাবে একটি সাংখ্যিক মান চয়ন করে
সৌরভ

8
আমি এপিআই প্রতিক্রিয়া থেকে একটি ফাইল ডাউনলোড করার জন্য উপরের কোডটি ব্যবহার করেছি তবে আমি ব্লব অংশটি তৈরি করতে কিছু ত্রুটি পাচ্ছি "টাইপ প্রতিক্রিয়া ব্লবপার্ট টাইপ করার যোগ্য নয়"। কেউ যদি এই সমস্যাটি জানে তবে দয়া করে সহায়তা করুন
00 এ নবিবিন

92

ব্যবহার করে দেখুন এই !

1 - প্রদর্শন পপ আপ সংরক্ষণ / ওপেনের জন্য নির্ভরতা ইনস্টল করুন

npm install file-saver --save
npm install @types/file-saver --save

2- ডেটা পুনরুদ্ধার করতে এই ফাংশন সহ একটি পরিষেবা তৈরি করুন

downloadFile(id): Observable<Blob> {
    let options = new RequestOptions({responseType: ResponseContentType.Blob });
    return this.http.get(this._baseUrl + '/' + id, options)
        .map(res => res.blob())
        .catch(this.handleError)
}

3- উপাদানটিতে ব্লবটি 'ফাইল-সেভার' দিয়ে পার্স করুন

import {saveAs as importedSaveAs} from "file-saver";

  this.myService.downloadFile(this.id).subscribe(blob => {
            importedSaveAs(blob, this.fileName);
        }
    )

এটি আমার পক্ষে কাজ করে!


1
আমি @ আলেজান্দ্রোর উত্তরটির সাথে একত্রে পদক্ষেপ 2 ব্যবহার করেছি এবং এটি ফাইল-সেভার ইনস্টল করার প্রয়োজন ছাড়াই কাজ করেছে ...
এভার্ট

5
ধন্যবাদ! এটি পুরোপুরি কাজ করে! আমি অবাক হয়েছি যে আমরা প্রতিক্রিয়া শিরোনামে সংজ্ঞায়িত ফাইলের নামটি পেতে পারি কিনা। এটা কি সম্ভব?
jfajunior

2
ত্রুটি Av5 'RequestOptions' টাইপের আর্গুমেন্ট '{শিরোনাম? টাইপের প্যারামিটারে অর্পণযোগ্য নয়?: HTTPHeilers | {[শিরোনাম: স্ট্রিং]: স্ট্রিং | স্ট্রিং []; };
জব

এটি বড় ফাইল ডাউনলোডের জন্য উপযুক্ত নয়।
রেভেন

61

আপনার যদি অনুরোধে শিরোনাম যুক্ত করার প্রয়োজন না হয় , Angular2 এ একটি ফাইল ডাউনলোড করতে আপনি একটি সহজ করতে পারেন :

window.location.href='http://example.com/myuri/report?param=x';

আপনার উপাদান মধ্যে।


4
কেউ দয়া করে বলতে পারেন কেন এই উত্তরটি নিম্নচাপযুক্ত? বিষয়টি হল কৌনিক 2 ব্যবহার করে একটি ফাইল ডাউনলোড করা। যদি এই পদ্ধতিটি একটি সাধারণ ডাউনলোড করতে কাজ করে তবে এটি একটি কার্যকর উত্তর হিসাবে চিহ্নিত করা উচিত।
সৌরভ শেঠি

5
@ সৌরভশেটি, আপনি কাস্টম শিরোনাম প্রেরণ করতে চান এমন ক্ষেত্রে এটি সহায়তা করবে না, যদি আপনি উদাহরণস্বরূপ একটি লেখার টোকেন প্রেরণ করতে চান? ওপি প্রশ্নের দিকে নজর দিলে আপনি দেখতে পাবেন যে সে ব্যবহার করে authHttp!
আকরাম

6
আমি ডাউনভোটগুলি বুঝতে পারি, তবুও এই উত্তরটি আমার সমস্যার সমাধান করেছে।
জোয়ারি শোয়েবি

1
আপনি যদি কোনও প্রসঙ্গে সার্ভারকে ইউআরএল ফিরিয়ে দিতে দেন তবে সার্ভারটি ইউআরএল প্রস্তুত করতে পারে। প্রাক্তন: অবজেক্ট: MyRecord.Cover। কভারটি সার্ভারের কোনও চিত্রের url হতে পারে। Get (Myrecord) কল করার সময় আপনি সার্ভারটিকে সুরক্ষা টোকেন এবং অন্যান্য শিরোনাম সেট সহ প্রস্তুত url (কভার) ফেরত দিতে দিন।
জেনস অ্যালেনিয়াস

2
এটি একটি উত্তর যে কাজ করে। কেবলমাত্র এটির << এখানে দরকারী বৈশিষ্ট্যটি সন্নিবেশ করান> যা এটি কোনও উত্তর নয় make
gburton

47

এটি HTTPClient এবং ফাইল-সেভার ব্যবহার করে কীভাবে করা যায় তা দেখার জন্য লোকেরা:

  1. ফাইল-সেভার ইনস্টল করুন

এনপিএম ফাইল-সেভার ইনস্টল করুন - সংরক্ষণ করুন

এনএমপি ইনস্টল করুন @ প্রকার / ফাইল-সেভার - সেভ করুন

API পরিষেবা শ্রেণি:

export() {
    return this.http.get(this.download_endpoint, 
        {responseType: 'blob'});
}

উপাদান:

import { saveAs } from 'file-saver';
exportPdf() {
    this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));
}

1
ডাউনলোড শুরু হলে ব্রাউজারে ফাইলাইজগুলি কীভাবে দেখানো যায়? আমি এইচটিএসপি শিরোনামে ফাইলসাইজকে সামগ্রী-দৈর্ঘ্য হিসাবে প্রেরণ করছি।
humbleCoder

35

এ কেমন?

this.http.get(targetUrl,{responseType:ResponseContentType.Blob})
        .catch((err)=>{return [do yourself]})
        .subscribe((res:Response)=>{
          var a = document.createElement("a");
          a.href = URL.createObjectURL(res.blob());
          a.download = fileName;
          // start download
          a.click();
        })

আমি এটি দিয়ে করতে পারে।
অতিরিক্ত প্যাকেজ দরকার নেই।


3
এত সহজ তবুও এটি এক যা নির্দোষভাবে কাজ করে। এটি ডোমকে বিশৃঙ্খলা করে না, কোনও উপাদান তৈরি করে না। আমি উপরের কয়েকটিটির সাথে এই সমাধানটি সংযুক্ত করেছি এবং এটি একটি কবজির মতো কাজ করে।
চ্যাক্স

20

যেমনটি আলেজান্দ্রো করিডোর উল্লেখ করেছেন এটি একটি সাধারণ স্কোপ ত্রুটি। subscribeঅ্যাসিঙ্ক্রোনাস চালানো হয় এবংopen যে প্রেক্ষাপটে এ স্থাপন করতে হবে, যাতে ডেটা লোডিং শেষ যখন আমরা ডাউনলোড আরম্ভ।

এটি বলেছিল, এটি করার দুটি উপায় আছে। দস্তাবেজগুলির পরামর্শ অনুযায়ী পরিষেবাটি ডেটা পাওয়ার এবং ম্যাপিংয়ের যত্ন নেয়:

//On the service:
downloadfile(runname: string, type: string){
  var headers = new Headers();
  headers.append('responseType', 'arraybuffer');
  return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .map(res => new Blob([res],{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }))
            .catch(this.logAndPassOn);
}

তারপরে, উপাদানটিতে আমরা কেবল সাবস্ক্রাইব করব এবং ম্যাপযুক্ত ডেটাগুলি নিয়ে ডিল করি। দুটি সম্ভাবনা রয়েছে। প্রথমটি , মূল পোস্টে প্রস্তাবিত হিসাবে, তবে আলেজান্দ্রো দ্বারা উল্লিখিত একটি ছোট সংশোধন প্রয়োজন:

//On the component
downloadfile(type: string){
  this.pservice.downloadfile(this.rundata.name, type)
      .subscribe(data => window.open(window.URL.createObjectURL(data)),
                  error => console.log("Error downloading the file."),
                  () => console.log('Completed file download.'));
  }

দ্বিতীয় উপায়টি হ'ল ফাইলরেডার ব্যবহার করা। লজিকটি একই তবে আমরা বাসা বাঁধে এবং এ্যাসিঙ্ক সমস্যা সমাধানের জন্য ডেটা লোড করার জন্য ফাইলআরডারটি স্পষ্টভাবে অপেক্ষা করতে পারি।

//On the component using FileReader
downloadfile(type: string){
    var reader = new FileReader();
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(res => reader.readAsDataURL(res), 
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    reader.onloadend = function (e) {
        window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no');
  }
}

দ্রষ্টব্য: আমি একটি এক্সেল ফাইল ডাউনলোড করার চেষ্টা করছি এবং ডাউনলোডটি ট্রিগার হওয়া সত্ত্বেও (সুতরাং এটি প্রশ্নের উত্তর দেয়) ফাইলটি দুর্নীতিগ্রস্থ। দূষিত ফাইল এড়ানোর জন্য এই পোস্টের উত্তর দেখুন


7
আমি মনে করি ফাইলটি দুর্নীতিগ্রস্থ হওয়ার কারণ হ'ল আপনি resব্লবের মধ্যে লোড করছেন এবং আপনি আসলে চান res._body। তবে _bodyএটি একটি ব্যক্তিগত পরিবর্তনশীল এবং অ্যাক্সেসযোগ্য নয়। আজকের হিসাবে .blob()এবং .arrayBuffer()একটি HTTP তে প্রতিক্রিয়া অবজেক্টটি কৌণিক 2 তে প্রয়োগ করা হয়নি text()এবং json()কেবলমাত্র দুটি বিকল্প রয়েছে তবে উভয়ই আপনার দেহকে পরিচ্ছন্ন করবে। আপনি কি সমাধান খুঁজে পেয়েছেন?
এস্কুয়েলার

1
হাই @ আরএল, আমি উপরের পদক্ষেপগুলি অনুসরণ করেছি এবং আমি সম্পূর্ণ হওয়ার সাথে সাথে সাবস্ক্রাইব করছি। তবুও ফাইলটি ডাউনলোড হতে দেখলাম না। আমি পাশাপাশি কোনও ত্রুটি দেখতে পেলাম না। দয়া করে সহায়তা করুন
ishশ অ্যাপ

1
2 টি বিকল্প আমাকে ফাইলটি ডাউনলোড করতে দেয়, তবে এটি প্রথমে পটভূমিতে ডেটা লোড করে। যদি আমার কাছে একটি বড় ফাইল ডাউনলোড করতে হয় তবে কী হবে?
f123

1
আমার সমাধানটি কেবল <a href=""></a>একটি ফাইল ডাউনলোড করতে ব্যবহার করা।
ব্যবহারকারী2061057

1
আমি জানি এটি একটি পুরানো উত্তর তবে এটি অনুসন্ধানের ফলাফলগুলিতে উচ্চতর এবং এটি স্বীকৃত উত্তর: লাইনটি ers Headers.append ('প্রতিক্রিয়া টাইপ', 'অ্যারেবুফার'); wrong ভুল। এটি একটি বিকল্প, শিরোনাম নয়। দয়া করে এটি ঠিক করুন। Aaaand ... শিরোনাম তৈরি করা হয় এবং ব্যবহৃত হয় না। সহায়ক নয়.
স্টিভো

17

কৌণিক ২.৪.x এর জন্য * .zip সমাধানটি ডাউনলোড করুন: আপনাকে অবশ্যই @ @ কৌনিক / HTTP থেকে রেসপন্স কনটেন্টটাইপটি আমদানি করতে হবে এবং প্রতিক্রিয়া টাইপটিকে রেসপন্স কনটেন্টটাইপ.আর্রে বাফারে পরিবর্তন করতে হবে (ডিফল্টরূপে এটি রেসপন্স কনটেন্টটাইপ.জসন)

getZip(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
 let headers = this.setHeaders({
      'Content-Type': 'application/zip',
      'Accept': 'application/zip'
    });

 return this.http.get(`${environment.apiUrl}${path}`, { 
   headers: headers, 
   search: params, 
   responseType: ResponseContentType.ArrayBuffer //magic
 })
          .catch(this.formatErrors)
          .map((res:Response) => res['_body']);
}

16

নতুন কৌণিক সংস্করণের জন্য:

npm install file-saver --save
npm install @types/file-saver --save


import {saveAs} from 'file-saver/FileSaver';

this.http.get('endpoint/', {responseType: "blob", headers: {'Accept': 'application/pdf'}})
  .subscribe(blob => {
    saveAs(blob, 'download.pdf');
  });

ধন্যবাদ, কৌনিক 8 এর সাথে কাজ করে। কেন এটি খুঁজে পাওয়া এত কঠিন ছিল তা জানেন না।
এমডিএভ

11

এজ্যাক্সের মাধ্যমে ফাইল ডাউনলোড করা সর্বদা একটি বেদনাদায়ক প্রক্রিয়া এবং আমার দৃষ্টিতে সার্ভার এবং ব্রাউজারকে সামগ্রীর ধরণের আলোচনার কাজটি করা দেওয়া ভাল।

আমি এটা সবচেয়ে ভাল মনে করি

<a href="api/sample/download"></a> 

এটা করতে। এমনকি এটির জন্য কোনও নতুন উইন্ডো খোলার এবং স্টাফের প্রয়োজন হয় না।

আপনার নমুনার মতো এমভিসি নিয়ন্ত্রক নীচের মতো হতে পারে:

[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()
{
    // ...
    return File(dataStream.ToArray(), "text/plain", "myblob.txt");
}

1
আপনি ঠিক বলেছেন, তবে কীভাবে আপনি একক পৃষ্ঠার অ্যাপ্লিকেশনটির মধ্যে সার্ভার ত্রুটিগুলি পরিচালনা করতে পারেন? কোনও ত্রুটির ক্ষেত্রে, সাধারণত, একটি আরএসটি পরিষেবা ত্রুটির সাথে জেএসএনকে ফেরত দেয়, ফলস্বরূপ অ্যাপ্লিকেশনটি অন্য ব্রাউজার উইন্ডোতে জেএসএনটি খুলতে সক্ষম হয় যা ব্যবহারকারী দেখতে চায় না
লুকা

2
আপনার যদি অ্যাক্সেস টোকেন থাকে তবে আপনাকে সরবরাহ করতে হবে এটি কাজ করে না
chris31389

এটি সহজ এবং সহজ। তবে আপনি যদি কিছু প্রমাণীকরণ করতে চান তবে এক সময়ের টোকেনের মতো কিছু হওয়ার সম্ভাবনা রয়েছে। সুতরাং, এটির পরিবর্তে এটির পরিবর্তে আপনার url থাকতে পারে: উদাহরণ.com / myuri / report?tokenid=1234-1233 এবং ডাটাবেসে টোকেন আইডি যাচাই করুন। অবশ্যই এটি সাধারণ পরিস্থিতি নয় এবং সমস্ত পরিস্থিতিতে কাজ করে তবে পরিস্থিতি সমাধান হতে পারে যেখানে স্ট্রিম হিসাবে রিপোর্ট ফিরিয়ে দেওয়ার আগে আপনার ডাটাবেসে অ্যাক্সেস থাকতে পারে ..
জিঞ্জার বিয়ার

সার্ভার থেকে ডাউনলোড url পান। সুতরাং সার্ভারটি এককালীন সুরক্ষা টোকেন সহ url প্রস্তুত করতে পারে।
জেনস অ্যালেনিয়াস

8

আমি 4.3 httpClient অবজেক্টের সাথে কৌনিক 4 ব্যবহার করছি। আমি জেএসের টেকনিক্যাল ব্লগের একটি উত্তর পেয়েছি যা লিঙ্ক অবজেক্ট তৈরি করে, ডাউনলোড করতে এটি ব্যবহার করে, তারপরে এটি ধ্বংস করে।

ক্লায়েন্ট:

doDownload(id: number, contentType: string) {
    return this.http
        .get(this.downloadUrl + id.toString(), { headers: new HttpHeaders().append('Content-Type', contentType), responseType: 'blob', observe: 'body' })
}

downloadFile(id: number, contentType: string, filename:string)  {

    return this.doDownload(id, contentType).subscribe(  
        res => { 
            var url = window.URL.createObjectURL(res);
            var a = document.createElement('a');
            document.body.appendChild(a);
            a.setAttribute('style', 'display: none');
            a.href = url;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(url);
            a.remove(); // remove the element
        }, error => {
            console.log('download error:', JSON.stringify(error));
        }, () => {
            console.log('Completed file download.')
        }); 

} 

এটি.ডাউনলোডআরল এর মানটি এপিআই-তে নির্দেশ করার জন্য আগে সেট করা হয়েছে। আমি এটি সংযুক্তিগুলি ডাউনলোড করতে ব্যবহার করছি, তাই আমি আইডি, বিষয়বস্তু টাইপ এবং ফাইলের নাম জানি: আমি ফাইলটি ফেরত দিতে এমভিসি এপি ব্যবহার করছি:

 [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
    public FileContentResult GetAttachment(Int32 attachmentID)
    { 
        Attachment AT = filerep.GetAttachment(attachmentID);            
        if (AT != null)
        {
            return new FileContentResult(AT.FileBytes, AT.ContentType);  
        }
        else
        { 
            return null;
        } 
    } 

সংযুক্তি ক্লাসটি দেখতে এরকম দেখাচ্ছে:

 public class Attachment
{  
    public Int32 AttachmentID { get; set; }
    public string FileName { get; set; }
    public byte[] FileBytes { get; set; }
    public string ContentType { get; set; } 
}

ফাইলরেপ সংগ্রহস্থল ডাটাবেস থেকে ফাইলটি ফিরিয়ে দেয়।

আশা করি এটি কাউকে সাহায্য করবে :)


7

যারা রেডাক্স প্যাটার্ন ব্যবহার করেন

আমি তার উত্তরে @ হেক্টর কিউভাস হিসাবে ফাইল-সেভারে যুক্ত করেছি। অ্যাঙ্গুলার 2 বনাম 2.3.1 ব্যবহার করে, আমাকে @ প্রকার / ফাইল-সেভারে যুক্ত করার দরকার নেই।

নিম্নলিখিত উদাহরণটি হল পিডিএফ হিসাবে একটি জার্নাল ডাউনলোড করা।

জার্নাল কর্ম

public static DOWNLOAD_JOURNALS = '[Journals] Download as PDF';
public downloadJournals(referenceId: string): Action {
 return {
   type: JournalActions.DOWNLOAD_JOURNALS,
   payload: { referenceId: referenceId }
 };
}

public static DOWNLOAD_JOURNALS_SUCCESS = '[Journals] Download as PDF Success';
public downloadJournalsSuccess(blob: Blob): Action {
 return {
   type: JournalActions.DOWNLOAD_JOURNALS_SUCCESS,
   payload: { blob: blob }
 };
}

জার্নাল প্রভাব

@Effect() download$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS)
    .switchMap(({payload}) =>
        this._journalApiService.downloadJournal(payload.referenceId)
        .map((blob) => this._actions.downloadJournalsSuccess(blob))
        .catch((err) => handleError(err, this._actions.downloadJournalsFail(err)))
    );

@Effect() downloadJournalSuccess$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS_SUCCESS)
    .map(({payload}) => saveBlobAs(payload.blob, 'journal.pdf'))

জার্নাল পরিষেবা

public downloadJournal(referenceId: string): Observable<any> {
    const url = `${this._config.momentumApi}/api/journals/${referenceId}/download`;
    return this._http.getBlob(url);
}

এইচটিটিপি পরিষেবা

public getBlob = (url: string): Observable<any> => {
    return this.request({
        method: RequestMethod.Get,
        url: url,
        responseType: ResponseContentType.Blob
    });
};

জার্নাল রিডুসার যদিও এটি কেবলমাত্র আমাদের প্রয়োগে ব্যবহৃত সঠিক রাজ্যগুলি সেট করে আমি এখনও সম্পূর্ণ প্যাটার্নটি দেখানোর জন্য এটি যুক্ত করতে চেয়েছিলাম wanted

case JournalActions.DOWNLOAD_JOURNALS: {
  return Object.assign({}, state, <IJournalState>{ downloading: true, hasValidationErrors: false, errors: [] });
}

case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: {
  return Object.assign({}, state, <IJournalState>{ downloading: false, hasValidationErrors: false, errors: [] });
}

আমি আশা করি এই সহায়ক।


7

আমি যে সমাধানটি আমাকে সহায়তা করেছিলাম তা ভাগ করি (যে কোনও উন্নতি প্রশংসিত হয়)

আপনার পরিষেবা 'প্রচার':

getMyFileFromBackend(typeName: string): Observable<any>{
    let param = new URLSearchParams();
    param.set('type', typeName);
    // setting 'responseType: 2' tells angular that you are loading an arraybuffer
    return this.http.get(http://MYSITE/API/FILEIMPORT, {search: params, responseType: 2})
            .map(res => res.text())
            .catch((error:any) => Observable.throw(error || 'Server error'));
}

উপাদান অংশ:

downloadfile(type: string){
   this.pservice.getMyFileFromBackend(typename).subscribe(
                    res => this.extractData(res),
                    (error:any) => Observable.throw(error || 'Server error')
                );
}

extractData(res: string){
    // transforme response to blob
    let myBlob: Blob = new Blob([res], {type: 'application/vnd.oasis.opendocument.spreadsheet'}); // replace the type by whatever type is your response

    var fileURL = URL.createObjectURL(myBlob);
    // Cross your fingers at this point and pray whatever you're used to pray
    window.open(fileURL);
}

উপাদান অংশে, আপনি কোনও প্রতিক্রিয়ার সাবস্ক্রাইব না করে পরিষেবাটি কল করেন। ওপেনঅফিস মাইম ধরণের সম্পূর্ণ তালিকার জন্য সাবস্ক্রাইব দেখুন: http://www.openoffice.org/framework/docamentation/mimetyype/mimetyype.html


7

আপনার ভিতরে নতুন পদ্ধতিটি কল করার চেষ্টা করা ভাল হবে better subscribe

this._reportService.getReport()
    .subscribe((data: any) => {
        this.downloadFile(data);
    },
        (error: any) => сonsole.log(error),
        () => console.log('Complete')
    );

ভিতরে downloadFile(data)ফাংশন আমাদের করা প্রয়োজনblock, link, href and file name

downloadFile(data: any, type: number, name: string) {
    const blob = new Blob([data], {type: 'text/csv'});
    const dataURL = window.URL.createObjectURL(blob);

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(blob);
      return;
    }

    const link = document.createElement('a');
    link.href = dataURL;
    link.download = 'export file.csv';
    link.click();

    setTimeout(() => {

      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(dataURL);
      }, 100);
    }
}

5

পিডিএফ ফাইলগুলি ডাউনলোড এবং দেখানোর জন্য , একটি খুব অনুরূপ কোড স্নিপড নীচের মত:

  private downloadFile(data: Response): void {
    let blob = new Blob([data.blob()], { type: "application/pdf" });
    let url = window.URL.createObjectURL(blob);
    window.open(url);
  }

  public showFile(fileEndpointPath: string): void {
    let reqOpt: RequestOptions = this.getAcmOptions();  //  getAcmOptions is our helper method. Change this line according to request headers you need.
    reqOpt.responseType = ResponseContentType.Blob;
    this.http
      .get(fileEndpointPath, reqOpt)
      .subscribe(
        data => this.downloadFile(data),
        error => alert("Error downloading file!"),
        () => console.log("OK!")
      );
  }

5

আমার ক্ষেত্রে আমি কিছু করেছি -

// service method
downloadFiles(vendorName, fileName) {
    return this.http.get(this.appconstants.filesDownloadUrl, { params: { vendorName: vendorName, fileName: fileName }, responseType: 'arraybuffer' }).map((res: ArrayBuffer) => { return res; })
        .catch((error: any) => _throw('Server error: ' + error));
}

// a controller function which actually downloads the file
saveData(data, fileName) {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    let blob = new Blob([data], { type: "octet/stream" }),
        url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
}

// a controller function to be called on requesting a download
downloadFiles() {
    this.service.downloadFiles(this.vendorName, this.fileName).subscribe(data => this.saveData(data, this.fileName), error => console.log("Error downloading the file."),
        () => console.info("OK"));
}

সমাধানটি এখানে থেকে উল্লেখ করা হয়েছে


4

পদক্ষেপ 2 এর জন্য ফাইল-সেভার এবং এইচটিটিপিপ্লায়েন্ট ব্যবহার করে হেক্টরের উত্তরে আপডেট করুন:

public downloadFile(file: File): Observable<Blob> {
    return this.http.get(file.fullPath, {responseType: 'blob'})
}

3

আমি বসন্ত এমভিসি এবং কৌণিক 2 ব্যবহার না করে দুর্নীতিগ্রস্থ না হয়ে কৌণিক 2 থেকে ডাউনলোড করার সমাধান পেয়েছি

1 ম- আমার রিটার্নের ধরনটি হ'ল: - জাভা প্রান্ত থেকে প্রতিক্রিয়াশক্তি । আমি এখানে বাইট প্রেরণ করছি [] অ্যারেতে নিয়ামকের কাছ থেকে রিটার্নের ধরণ রয়েছে।

২ য়- আপনার কর্মক্ষেত্রের ইনডেক্স পৃষ্ঠায় ফাইলসভারটি অন্তর্ভুক্ত করার জন্য:

<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>

তৃতীয়- উপাদানগুলির টিএসে এই কোডটি লিখুন:

import {ResponseContentType} from '@angular.core';

let headers = new Headers({ 'Content-Type': 'application/json', 'MyApp-Application' : 'AppName', 'Accept': 'application/pdf' });
        let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob });
            this.http
            .post('/project/test/export',
                    somevalue,options)
              .subscribe(data => {

                  var mediaType = 'application/vnd.ms-excel';
                  let blob: Blob = data.blob();
                    window['saveAs'](blob, 'sample.xls');

                });

এটি আপনাকে xls ফাইলের ফর্ম্যাট দেবে। আপনি যদি অন্য ফর্ম্যাটগুলি চান তবে ডান এক্সটেনশন সহ মিডিয়াটাইপ এবং ফাইলের নাম পরিবর্তন করুন।


3

আমি আজ এই একই মামলার মুখোমুখি হয়েছি, আমাকে একটি সংযুক্তি হিসাবে একটি পিডিএফ ফাইল ডাউনলোড করতে হয়েছিল (ফাইলটি ব্রাউজারে রেন্ডার করা উচিত নয়, পরিবর্তে ডাউনলোড করা উচিত)। আমি আবিষ্কার করেছি যে এটি আবিষ্কার করতে আমাকে ফাইলটি একটি কৌণিকের মধ্যে পেতে Blobহয়েছিল এবং একই সময়ে, Content-Dispositionপ্রতিক্রিয়ায় একটি শিরোনাম যুক্ত করতে হয়েছিল।

এটি আমিই পেতে পারি সবচেয়ে সহজ (কৌণিক)):

পরিষেবা ভিতরে:

getFile(id: String): Observable<HttpResponse<Blob>> {
  return this.http.get(`./file/${id}`, {responseType: 'blob', observe: 'response'});
}

তারপরে, যখন আমার কোনও উপাদানগুলিতে ফাইলটি ডাউনলোড করা দরকার তখন আমি কেবল:

fileService.getFile('123').subscribe((file: HttpResponse<Blob>) => window.location.href = file.url);

হালনাগাদ:

পরিষেবা থেকে অপ্রয়োজনীয় শিরোনাম সেটিং সরানো হয়েছে


যদি আমি উইন্ডো.ওপেনের পরিবর্তে উইন্ডো.লোকেশন.ইরেফ ব্যবহার করি তবে ক্রোম এটিকে একাধিক ফাইল ডাউনলোড হিসাবে বিবেচনা করে।
ড্যানো

আপনার যদি শিরোনামে
লেখক

3

নিম্নলিখিত কোডটি আমার পক্ষে কাজ করেছিল

let link = document.createElement('a');
link.href = data.fileurl; //data is object received as response
link.download = data.fileurl.substr(data.fileurl.lastIndexOf('/') + 1);
link.click();

2

আমি এখনও পর্যন্ত উত্তরগুলি অন্তর্দৃষ্টি এবং সতর্কতার অভাব পেয়েছি। আপনি IE10 + (যদি যত্নশীল হন) এর সাথে অসঙ্গতিগুলির জন্য নজর রাখতে পারেন এবং।

এটি অ্যাপ্লিকেশন অংশ এবং পরিষেবা অংশের পরে সম্পূর্ণ উদাহরণ। নোট করুন যে আমরা ফাইল নামটির জন্য শিরোনাম ধরার জন্য পর্যবেক্ষণ: "প্রতিক্রিয়া" সেট করেছি । এছাড়াও নোট করুন যে কন্টেন্ট-ডিসপজিশন শিরোনামটি সার্ভারের দ্বারা সেট এবং উন্মুক্ত করতে হবে, অন্যথায় বর্তমান কৌণিক এইচটিপিপিলেট এটি পাস করবে না। আমি নীচে এর জন্য কোডের একটি ডটনেট কোর টুকরা যুক্ত করেছি ।

public exportAsExcelFile(dataId: InputData) {
    return this.http.get(this.apiUrl + `event/export/${event.id}`, {
        responseType: "blob",
        observe: "response"
    }).pipe(
        tap(response => {
            this.downloadFile(response.body, this.parseFilename(response.headers.get('Content-Disposition')));
        })
    );
}

private downloadFile(data: Blob, filename: string) {
    const blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8;'});
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    } else {
        const link = document.createElement('a');
        if (link.download !== undefined) {
            // Browsers that support HTML5 download attribute
            const url = URL.createObjectURL(blob);
            link.setAttribute('href', url);
            link.setAttribute('download', filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

private parseFilename(contentDisposition): string {
    if (!contentDisposition) return null;
    let matches = /filename="(.*?)"/g.exec(contentDisposition);

    return matches && matches.length > 1 ? matches[1] : null;
}

কনটেন্ট-ডিসপজিশন এবং মিডিয়া টাইপ সহ ডটনেট কোর

 private object ConvertFileResponse(ExcelOutputDto excelOutput)
    {
        if (excelOutput != null)
        {
            ContentDisposition contentDisposition = new ContentDisposition
            {
                FileName = excelOutput.FileName.Contains(_excelExportService.XlsxExtension) ? excelOutput.FileName : "TeamsiteExport.xlsx",
                Inline = false
            };
            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
            Response.Headers.Add("Content-Disposition", contentDisposition.ToString());
            return File(excelOutput.ExcelSheet, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        }
        else
        {
            throw new UserFriendlyException("The excel output was empty due to no events.");
        }
    }

1
 let headers = new Headers({
                'Content-Type': 'application/json',
                'MyApp-Application': 'AppName',
                'Accept': 'application/vnd.ms-excel'
            });
            let options = new RequestOptions({
                headers: headers,
                responseType: ResponseContentType.Blob
            });


this.http.post(this.urlName + '/services/exportNewUpc', localStorageValue, options)
                .subscribe(data => {
                    if (navigator.appVersion.toString().indexOf('.NET') > 0)
                    window.navigator.msSaveBlob(data.blob(), "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+".xlsx");

                    else {
                        var a = document.createElement("a");
                        a.href = URL.createObjectURL(data.blob());
                        a.download = "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+ ".xlsx";
                        a.click();
                    }
                    this.ui_loader = false;
                    this.selectedexport = 0;
                }, error => {
                    console.log(error.json());
                    this.ui_loader = false;
                    document.getElementById("exceptionerror").click();
                });

1

সহজ ভাবে বললে urlযেমন hrefনীচের হিসাবে।

<a href="my_url">Download File</a>

এটা কি কাজ করে? আমি ত্রুটি পেয়েছি ... "ERROR TypeError:" স্ক্রিপ্ট থেকে 'ফাইল: ///Downloads/test.json' অ্যাক্সেস প্রত্যাখ্যান করা হয়েছে। ""
জয়

ধন্যবাদ আপনি কীভাবে ইউআরএল দেখতে দেখতে ভাগ করে নিতে পারেন? আইডি এটি ফাইল প্রোটোকল বা HTTP বা অন্য কিছু?
জয়

এটি ফাইল প্রোটোকল।
হারুনুর রশিদ

1

<a href="my_url" download="myfilename">Download file</a>

আমার_আরএলটির একই উত্স হওয়া উচিত, অন্যথায় এটি সেই জায়গায় ফিরে যাবে


হ্যাঁ, এটি খাঁটি এইচটিএমএল-এর জন্য কাজ করবে, অন্যান্য জটিল স্টাফগুলি কেন সন্ধান করা উচিত তা নিশ্চিত নয়
ভিভাইক সিংহ


1

আপনি নিজের টেম্পলেট থেকে সরাসরি এমন কোনও ফাইল ডাউনলোড করতে পারেন যেখানে আপনি ডাউনলোডের বৈশিষ্ট্যটি ব্যবহার [attr.href]করেন এবং উপাদান থেকে কোনও সম্পত্তি মান সরবরাহ করতে পারেন। এই সহজ সমাধানটি বেশিরভাগ ব্রাউজারে কাজ করা উচিত।

<a download [attr.href]="yourDownloadLink"></a>

তথ্যসূত্র: https://www.w3schools.com/tags/att_a_download.asp


1
তাই আপনাকে স্বাগতম! আমার (টাইপসেটিং এবং ব্যাকরণ) সংশোধনগুলি সহায়ক কিনা তা পরীক্ষা করে দেখুন।
বি - রিয়ান

0

আপনি যদি কোনও URL এ কেবলমাত্র পরামিতিগুলি প্রেরণ করেন তবে আপনি এটি এইভাবে করতে পারেন:

downloadfile(runname: string, type: string): string {
   return window.location.href = `${this.files_api + this.title +"/"+ runname + "/?file="+ type}`;
}

পরিষেবা যে পরামিতি প্রাপ্ত


0

এই উত্তরটি পরামর্শ দেয় আপনি প্রাথমিকভাবে সুরক্ষার কারণে ফাইলগুলি সরাসরি এজেএক্সের সাথে ডাউনলোড করতে পারবেন না। সুতরাং আমি এই পরিস্থিতিতে আমি কি করব তা বর্ণনা করব,

01. যোগ hrefআপনার নোঙ্গর ট্যাগে অ্যাট্রিবিউট ভিতরে component.htmlফাইল,
যেমন: -

<div>
       <a [href]="fileUrl" mat-raised-button (click)='getGenaratedLetterTemplate(element)'> GENARATE </a>
</div>

02.component.ts সুরক্ষা স্তরটি বাইপাস করতে এবং সেভকে পপআপ ডায়ালগ হিসাবে আনতে আপনার নিম্নলিখিত সমস্ত পদক্ষেপগুলি করুন ,
যেমন: -

import { environment } from 'environments/environment';
import { DomSanitizer } from '@angular/platform-browser';
export class ViewHrApprovalComponent implements OnInit {
private apiUrl = environment.apiUrl;
  fileUrl
 constructor(
    private sanitizer: DomSanitizer,
    private letterService: LetterService) {}
getGenaratedLetterTemplate(letter) {

    this.data.getGenaratedLetterTemplate(letter.letterId).subscribe(
      // cannot download files directly with AJAX, primarily for security reasons);
    console.log(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
    this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
  }

দ্রষ্টব্য: আপনি যদি স্ট্যাটাস কোড 200 সহ কোনও ত্রুটি "ওকে" পেয়ে থাকেন তবে এই উত্তরটি কার্যকর হবে


0

ঠিক আছে, আমি উপরের উত্তরগুলির দ্বারা অনুপ্রাণিত এক কোডের টুকরো লিখেছি যা বেশিরভাগ পরিস্থিতিতে সহজেই কাজ করা উচিত যেখানে সার্ভারটি কোনও তৃতীয় পক্ষের ইনস্টলেশন ছাড়াই, কোনও তৃতীয় পক্ষের ইনস্টলেশন ব্যতীত কোনও বিষয়বস্তু প্রদর্শন শিরোনাম সহ কোনও ফাইল প্রেরণ করে।

প্রথমত, কীভাবে আপনার উপাদান ফাইল থেকে কোড কল করবেন

this.httpclient.get(
   `${myBackend}`,
   {
      observe: 'response',
      responseType: 'blob'
   }
).pipe(first())
.subscribe(response => SaveFileResponse(response, 'Custom File Name.extension'));

আপনি দেখতে পাচ্ছেন, এটি মূলত দুটি পরিবর্তন সহ কৌনিক থেকে গড় ব্যাকএন্ড কল call

  1. আমি শরীরের পরিবর্তে প্রতিক্রিয়া পর্যবেক্ষণ করছি
  2. আমি প্রতিক্রিয়া একটি ব্লব হচ্ছে সম্পর্কে স্পষ্ট করা হচ্ছে

একবার সার্ভার থেকে ফাইলটি আনার পরে, আমি নীতিগতভাবে, ফাইলটি সংরক্ষণকারীর পুরো কাজটি হেল্পার ফাংশনে অর্পণ করি, যা আমি একটি পৃথক ফাইলে রেখেছি এবং যে কোনও উপাদানটিতে আমদানি করতে হবে

export const SaveFileResponse = 
(response: HttpResponse<Blob>, 
 filename: string = null) => 
{
    //null-checks, just because :P
    if (response == null || response.body == null)
        return;

    let serverProvidesName: boolean = true;
    if (filename != null)
        serverProvidesName = false;

    //assuming the header is something like
    //content-disposition: attachment; filename=TestDownload.xlsx; filename*=UTF-8''TestDownload.xlsx
    if (serverProvidesName)
        try {
            let f: string = response.headers.get('content-disposition').split(';')[1];
            if (f.includes('filename='))
                filename = f.substring(10);
        }
        catch { }
    SaveFile(response.body, filename);
}

//Create an anchor element, attach file to it, and
//programmatically click it. 
export const SaveFile = (blobfile: Blob, filename: string = null) => {
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blobfile);
    a.download = filename;
    a.click();
}

সেখানে আর কোনও ক্রিপ্টিক জিইউইডি ফাইলের নাম নেই! আমরা ক্লায়েন্টে স্পষ্টভাবে নির্দিষ্ট করে না দিয়ে সার্ভারের যে নামই সরবরাহ করে তা ব্যবহার করতে পারি, বা সার্ভারের দেওয়া ফাইলের নামটি ওভাররাইট করে (এই উদাহরণ হিসাবে)। এছাড়াও, সহজে প্রয়োজন মতো, প্রয়োজন অনুসারে বিষয়বস্তু-বিবরণ থেকে ফাইলের নাম বের করার অ্যালগরিদমটি তাদের প্রয়োজন অনুসারে পরিবর্তন করতে পারে এবং অন্য সমস্ত কিছুই ক্ষতিগ্রস্থ থাকবে - যেমন নিষ্কাশনের সময় ত্রুটির ক্ষেত্রে এটি কেবল 'নাল' পাস করবে ফাইলের নাম হিসাবে।

অন্য একটি উত্তর ইতিমধ্যে নির্দেশিত হিসাবে, IE এর মতো বরাবরের মতো কিছু বিশেষ চিকিত্সা প্রয়োজন। তবে কয়েকমাসে ক্রোমিয়াম প্রান্ত আসার সাথে সাথে আমি নতুন অ্যাপ্লিকেশনগুলি তৈরি করার সময় এটি নিয়ে চিন্তা করব না (আশা করি)। ইউআরএলটি প্রত্যাহার করার বিষয়টিও রয়েছে, তবে আমি এ সম্পর্কে নিশ্চিত-ততটা নিশ্চিত নই, তাই যদি কেউ মন্তব্যগুলিতে এটির সাথে সহায়তা করতে পারে তবে তা দুর্দান্ত।


0

যদি কোনও ট্যাব খোলা থাকে এবং কিছু না ডাউনলোড করে বন্ধ হয়, আমি মক অ্যাঙ্কর লিঙ্কটি অনুসরণ করার চেষ্টা করেছি এবং এটি কার্যকর হয়েছে।

downloadFile(x: any) {
var newBlob = new Blob([x], { type: "application/octet-stream" });

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(newBlob);
      return;
    }

    // For other browsers: 
    // Create a link pointing to the ObjectURL containing the blob.
    const data = window.URL.createObjectURL(newBlob);

    var link = document.createElement('a');
    link.href = data;
    link.download = "mapped.xlsx";
    // this is necessary as link.click() does not work on the latest firefox
    link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));

    setTimeout(function () {
      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(data);
      link.remove();
    }, 100);  }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.