How to Download Attachment Or Files Using Lightning Web Components (LWC) in Salesforce Lightning Experience (LEX)

How to Download Attachment Or Files Using Lightning Web Components (LWC) in Salesforce Lightning Experience (LEX)

Introduction

File handling is an essential part of many Salesforce applications, especially when working with records like Accounts, Opportunities, or Cases. Salesforce developers frequently implement file download features in Lightning Web Components (LWC). They do this when downloading an file from a record or fetching content from an external system.

In this blog, we’ll explore below powerful methods for downloading files using LWC:

  1. Download Files from Attachments Object
  2. Download Files From Files Object (Content Document)
  3. Downloading files by calling a REST API directly from LWC

🎯 Scenario 1: Download Files from Attachments Object

Use Case 1- Download Single File From Attachment

a) Example – Using Salesforce URL in browser

This example shows you how to download the most recent Attachment related to a Salesforce Account record.It uses a standard Salesforce file download URL. The URL is constructed in Apex. It is returned to the Lightning Web Component. The component then opens it in the browser to trigger the download.

This approach is ideal for working with native Salesforce attachments. It helps avoid dealing with file blobs or base64 encoding. It leverages Salesforce’s built-in file-serving capabilities via the /servlet/servlet.FileDownload endpoint.

<template>
<lightning-card title="Download Single Attachment" icon-name="utility:download">
<div class="slds-p-around_medium">
<template if:true={isLoading}>
<lightning-spinner alternative-text="Loading …" size="medium"></lightning-spinner>
</template>
<lightning-button
label="Download"
onclick={handleDownload}
disabled={isLoading}
></lightning-button>
</div>
</lightning-card>
</template>
/**
* What it does:
The component uses the recordId passed as parameter to the apex class, to get the related attachments.
When the Download button is clicked:
A loading spinner is shown by setting isLoading = true.
It calls the Apex method to get the file download URL.
It uses window.open() to open the download link in a new browser tab or window.
Spinner is hidden once the download link is opened or in case of an error.
*/
import { LightningElement, api } from 'lwc';
import getAttachmentDownloadUrl from '@salesforce/apex/FilesDownloaderCtrl.getAttachmentDownloadUrl';
export default class FilesDownloader extends LightningElement {
@api recordId;
isLoading = false;
/**
* @description: handleDownload method handles the download of attachments related to the record.
* It uses the recordId to fetch the download URL of Attachment from Apex
* and triggers the download process.
* @returns {Promise<void>} – Resolves when the download is complete or fails.
*/
async handleDownload() {
// Set loading state to true to show spinner
this.isLoading = true;
try {
// Call Apex method to fetch the download URL for the attachments related to the recordId
// this waits for Apex method to return the download URL
const url = await getAttachmentDownloadUrl({ parentId: this.recordId });
// Open the download link in a new tab or window
window.open(url, '_blank');
} catch (error) {
// Handle any errors that occur during the download process
console.error('Download error:', error);
} finally {
// Ensure the loading state is reset regardless of success or failure
this.isLoading = false;
}
}
}
/**
* What it does:
The method getAttachmentDownloadUrl():
Accepts a parentId, which is the ID of the record (e.g., a Case or Account) whose attachment we want to fetch.
Queries for the most recently modified Attachment associated with that record.
Constructs and returns a standard Salesforce file download URL using the Attachment ID:
/servlet/servlet.FileDownload?file=<AttachmentId>
This URL will trigger the browser to download the file when opened.
*/
public with sharing class FilesDownloaderCtrl {
/**
* @description: This method retrieves the download URL for the latest attachment related to a given record.
* @param parentId – The ID of the record for which the latest attachment's download URL is to be fetched.
* @returns {String} – The download URL of the latest
*/
@AuraEnabled
public static String getAttachmentDownloadUrl(Id parentId) {
// Query the latest attachment related to the given parentId
Attachment att = [SELECT Id, Name FROM Attachment WHERE parentId = :parentId ORDER BY LastModifiedDate DESC LIMIT 1];
// Return the download URL for the latest attachment found
return '/servlet/servlet.FileDownload?file=' + att.Id;
}
}
When to Use This Approach:
  • You’re working with standard Attachment objects (not ContentDocument).
  • You want to avoid using fetch() or manually handling file streams.
  • You want a clean, server-assisted file download mechanism.
  • The file is already stored in Salesforce, and you simply need to expose a download link.

b) Example – Using Base64 File

This example demonstrates how to download the most recent Attachment related to a Salesforce record using base64 encoding. The file is fetched from an Apex controller as a base64-encoded string. It is then converted into a Blob. Finally, it is downloaded directly in the browser.

This approach is ideal when you need to inject client-side logic before triggering the download. For instance, you might handle filename formatting, validation, or enforcing conditional access. It gives you full control over the download process through JavaScript. This control makes it particularly useful for mobile-first applications. It is also beneficial in scenarios that require enhanced interactivity and flexibility on the client side.

<template>
<lightning-card title="Download Single Attachment" icon-name="utility:download">
<div class="slds-p-around_medium">
<template if:true={isLoading}>
<lightning-spinner alternative-text="Loading …" size="medium"></lightning-spinner>
</template>
<lightning-button
label="Download"
onclick={handleDownload}
disabled={isLoading}
></lightning-button>
</div>
</lightning-card>
</template>
/**
* What it does:
The component uses the recordId passed as parameter to the apex class, to get the related attachments.
When the user clicks the Download button:
It shows a loading spinner.
Calls the Apex method to fetch the latest attachment’s metadata and base64 content.
Converts the base64 string back to a binary Blob object using the base64ToBlob() method.
Creates a temporary anchor (<a>) element to trigger the file download with a proper filename.
Cleans up the blob URL and DOM after the download is triggered.
*/
import { LightningElement, api } from 'lwc';
import getAttachmentAsBlob from '@salesforce/apex/FilesDownloaderCtrl.getAttachmentAsBlob';
export default class FilesDownloader extends LightningElement {
@api recordId;
isLoading = false;
/**
* @description: handleDownload method handles the download of the latest attachment for the given recordId.
* It fetches the attachment data from Apex, converts it to a Blob,
* and triggers the download process.
* @returns {Promise<void>} – Resolves when the download is complete or fails.
*/
async handleDownload() {
// Set loading state to true to show spinner
this.isLoading = true;
try {
// Call Apex method to get the attachment as Blob
// this waits for Apex to return the blob
const result = await getAttachmentAsBlob({ parentId: this.recordId });
// Convert Base64 to Blob
const blob = this.base64ToBlob(result.base64Data, result.mimeType || 'application/octet-stream');
// Create a URL for the Blob
const url = URL.createObjectURL(blob);
// Create a temporary anchor element to trigger the download
const link = document.createElement('a');
// Set the href and download attributes
link.href = url;
// Use the filename from the result or a default name
link.download = result.fileName || 'DownloadedFile';
// Append the link to the body
document.body.appendChild(link);
// Trigger the download
link.click();
// Clean up: remove the link and revoke the object URL
document.body.removeChild(link);
// Revoke the object URL to free up memory
URL.revokeObjectURL(url);
} catch (error) {
// Handle any errors that occur during the download process
console.error('Download error:', error);
} finally {
// Ensure the loading state is reset regardless of success or failure
this.isLoading = false;
}
}
/**
* @description – Converts a Base64 string to a Blob object.
* @param {string} base64 – The Base64 encoded string.
* @param {string} mimeType – The MIME type of the file.
* @returns {Blob} – The Blob object created from the Base64 string.
*/
base64ToBlob(base64, mimeType) {
// Decode Base64 string to binary data
const byteChars = atob(base64);
// Create a Uint8Array from the binary data
const byteArray = new Uint8Array(byteChars.length);
// Loop through the binary data and fill the Uint8Array
for (let i = 0; i < byteChars.length; i++) {
// Convert each character to its char code and assign it to the Uint8Array
byteArray[i] = byteChars.charCodeAt(i);
}
// Create and return a Blob object with the binary data and specified MIME type
return new Blob([byteArray], { type: mimeType });
}
}
/**
* What it does:
Defines a nested FileWrapper class that holds:
fileName: Name of the file.
mimeType: MIME type like application/pdf or image/png.
base64Data: Encoded file content as a base64 string.
The method getAttachmentAsBlob():
Fetches the most recently updated Attachment record for the given parent record.
Converts the binary content (Body) to base64.
Wraps it into a FileWrapper object and returns it to the frontend.
*/
public with sharing class FilesDownloaderCtrl {
public class FileWrapper {
@AuraEnabled public String fileName;
@AuraEnabled public String mimeType;
@AuraEnabled public String base64Data;
}
/**
* @description – Fetches the most recent Attachment for a given parent record ID.
* Returns the attachment data wrapped in a FileWrapper object.
* @param – parentId The ID of the parent record to fetch attachments for.
* @return – A FileWrapper object containing the attachment details.
*/
@AuraEnabled
public static FileWrapper getAttachmentAsBlob(Id parentId) {
Attachment att = [
SELECT Id, Name, Body, ContentType
FROM Attachment
WHERE ParentId = :parentId
ORDER BY LastModifiedDate DESC
LIMIT 1
];
// Create a FileWrapper object to return
FileWrapper fileData = new FileWrapper();
fileData.fileName = att.Name;
fileData.mimeType = att.ContentType;
fileData.base64Data = EncodingUtil.base64Encode(att.Body);
return fileData;
}
}
When to Use This Approach:
  • You want to avoid opening a new browser tab with window.open().
  • You need to customize the file download logic (e.g., add watermarking, enforce client-side conditions).
  • You’re working with older Salesforce attachments and not ContentDocument-based Files.
  • You’re building for environments like Salesforce Mobile App, where URL-based downloads may not work as reliably.
Use Case 2- Downloading All Attachments as a ZIP

When working with Salesforce records like Accounts, it’s common to have multiple related attachments. This example demonstrates how to let users download all attachments as a single ZIP file with a simple click.

<template>
<lightning-card title="Download All Attachment As Zip" icon-name="utility:download">
<div class="slds-p-around_medium">
<template if:true={isLoading}>
<lightning-spinner alternative-text="Loading ZIP…" size="medium"></lightning-spinner>
</template>
<lightning-button
label="Download"
onclick={handleDownload}
disabled={isLoading}
></lightning-button>
</div>
</lightning-card>
</template>
/**
* What it does:
The component used the recordId passed as parameter to the Apex class, to get the related attachments.
When the Download button is clicked:
A loading spinner is shown by setting isLoading = true.
It calls the Apex method to get the attachments as a ZIP file.
It converts the Base64-encoded ZIP file to a Blob.
It creates a download link for the ZIP file and triggers the download.
Spinner is hidden once the download is initiated or in case of an error.
*/
import { LightningElement, api } from 'lwc';
import getAttachmentsAsZip from '@salesforce/apex/FilesDownloaderCtrl.getAttachmentsAsZip';
export default class FilesDownloader extends LightningElement {
@api recordId;
isLoading = false;
/**
* @description: handleDownload method handles the download of attachments related to the record.
* It uses the recordId to fetch the download URL of Attachment from Apex
* and triggers the download process.
* @returns {Promise<void>} – Resolves when the download is complete or fails.
*/
async handleDownload() {
debugger;
this.isLoading = true;
try {
// Call Apex method to get the Base64-encoded ZIP file
// this waits for Apex method to return the Base64-encoded ZIP file
const base64Zip = await getAttachmentsAsZip({ parentId: this.recordId });
// Convert Base64 to Blob
const blob = this.base64ToBlob(base64Zip, 'application/zip');
// Create a URL for the Blob
const url = URL.createObjectURL(blob);
// Create a temporary anchor element to trigger the download
const link = document.createElement('a');
// Set the href and download attributes
link.href = url;
// Set the filename for the downloaded file
link.download = 'Attachments.zip';
// Trigger the download
link.click();
// Clean up the URL object
URL.revokeObjectURL(url);
} catch (error) {
// Handle any errors that occur during the download process
console.error('Download failed:', error);
} finally {
// Ensure the loading state is reset regardless of success or failure
this.isLoading = false;
}
}
/**
* @description: Converts a Base64-encoded string to a Blob object.
* @param {string} base64 – The Base64-encoded string to convert.
* @param {string} mime – The MIME type of the Blob.
* @returns {Blob} – The resulting Blob object.
*/
base64ToBlob(base64, mime) {
// Decode Base64 string to binary data
const byteChars = atob(base64);
// Create a Uint8Array from the binary data
const byteNumbers = new Array(byteChars.length);
// Loop through the binary data and convert it to byte numbers
for (let i = 0; i < byteChars.length; i++) {
// Convert each character to its char code
byteNumbers[i] = byteChars.charCodeAt(i);
}
// Create a Uint8Array from the byte numbers and return as a Blob
const byteArray = new Uint8Array(byteNumbers);
// return a new Blob object with the byte array and specified MIME type
return new Blob([byteArray], { type: mime });
}
}
/**
* What it does:
The method getAttachmentsAsZip():
Accepts a parentId, which is the ID of the record (e.g., a Case or Account) whose attachments we want to fetch.
Queries for all attachments associated with that record.
Uses the Compression.ZipWriter to create a ZIP archive containing all attachments.
Returns the ZIP file as a Base64-encoded string, which can be used in a Lightning Web Component (LWC) to trigger a download.
*/
public with sharing class FilesDownloaderCtrl {
/**
* @description: This method retrieves all attachments related to a given record and returns them as a ZIP file.
* @param parentId – The ID of the record for which attachments are to be fetched.
* @returns {String} – A Base64-encoded string representing the ZIP file containing
*/
@AuraEnabled
public static String getAttachmentsAsZip(Id parentId) {
// Retrieve all attachments related to the specified parentId
List<Attachment> attachments = [
SELECT Id, Name, Body FROM Attachment
WHERE ParentId = :parentId
];
// Declare a map to hold file names and their corresponding Blob data
Map<String, Blob> fileMap = new Map<String, Blob>();
// Loop through the attachments and populate the fileMap
for (Attachment attachmentFile : attachments) {
fileMap.put(attachmentFile.Name, attachmentFile.Body);
}
// Declare a ZipWriter to create the ZIP archive
Compression.ZipWriter zip = new Compression.ZipWriter();
// Loop through the fileMap and add each file to the ZIP archive
for (String fileName : fileMap.keySet()) {
zip.addEntry(fileName, fileMap.get(fileName));
}
// Get the ZIP archive as a Blob
Blob zipBlob = zip.getArchive();
// Return the ZIP file as a Base64-encoded string
return EncodingUtil.base64Encode(zipBlob);
}
}

🎯 Scenario 2: Download Files From Files Object (Content Document)

Use Case 1- Download Single File From Content Version

This example shows how to download the most recent File related to a Salesforce record. It is stored as a ContentDocument / ContentVersion. This is done using a base64-to-blob approach. The file is retrieved from the Salesforce Files object. It is encoded as a Base64 string via Apex. Then it is converted to a Blob in JavaScript. Finally, it is downloaded in the browser.

This approach is ideal for modern file storage in Salesforce. It supports the ContentDocument model, which is used by Lightning Files and Notes. It also gives you fine-grained control over the download process within an LWC.

.spinner-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 120px; /* Adjust as needed for vertical centering */
width: 100%;
}
.spinner-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1rem;
font-weight: bold;
color: black;
}
<template>
<lightning-card title="Download File From Files (Content Document)" icon-name="utility:download">
<div class="slds-p-around_medium">
<template if:true={isLoading}>
<div class="spinner-container">
<lightning-spinner alternative-text="Loading ZIP…" size="large"></lightning-spinner>
<div class="spinner-text">{progress}%</div>
</div>
</template>
<lightning-button
label="Download"
onclick={handleDownload}
disabled={isLoading}
></lightning-button>
</div>
</lightning-card>
</template>
/**
* What it does:
The component uses the recordId passed as parameter to the apex class, to get the related files
When the user clicks the Download button:
It shows a loading spinner.
It calls the Apex method getFileAsBlob to get the file as a Blob.
It creates a download link and triggers the download.
It updates the progress of the download.
*/
/* eslint-disable @lwc/lwc/no-async-operation */
/* eslint-disable no-await-in-loop */
import { LightningElement, api } from 'lwc';
import getFileAsBlob from '@salesforce/apex/FilesDownloaderCtrl.getFileAsBlob';
export default class FilesDownloader extends LightningElement {
@api recordId;
isLoading = false;
progress = 0; // Initialize progress to 0
/**
* @description – handleDownload method handles the download of a file associated with a record.
* It retrieves the file as a Blob from the server, creates a download link, and triggers the download.
* @returns {Promise<void>} – Resolves when the download is complete or fails.
*/
async handleDownload() {
this.isLoading = true;
this.progress = 0; // Reset progress
try {
// Call the Apex method to get the file as a Blob
const result = await getFileAsBlob({ parentId: this.recordId });
// Convert Base64 to Blob
const blob = await this.base64ToBlob(result.base64Data, result.mimeType || 'application/octet-stream');
// Create a URL for the Blob
const url = URL.createObjectURL(blob);
// Create a temporary anchor element to trigger the download
const link = document.createElement('a');
// Set the href
link.href = url;
// Use the filename from the result or a default name
link.download = result.fileName || 'DownloadedFile';
// Append the link to the body
document.body.appendChild(link);
// Trigger the download
link.click();
// Clean up: remove the link and revoke the object URL
document.body.removeChild(link);
// Revoke the object URL to free up memory
URL.revokeObjectURL(url);
} catch (error) {
// Handle any errors that occur during the download process
console.error('Download error:', error);
} finally {
// Ensure the loading state is reset regardless of success or failure
this.isLoading = false;
// Reset progress to 0
this.progress = 0;
}
}
/**
* @description – Converts a base64 string to a Blob object.
* @param {string} base64 – The base64 encoded string of the file.
* @param {string} mimeType – The MIME type of the file.
* @returns {Promise<Blob>} – A Promise that resolves to a Blob object.
*/
async base64ToBlob(base64, mimeType) {
// Decode the base64 string
const byteChars = atob(base64);
// Calculate the total number of bytes
const totalBytes = byteChars.length;
//Declare chunk size based on file size
let chunkSize;
// Calculate the file size in MB
const fileSizeMB = totalBytes / (1024 * 1024);
// Set chunk size dynamically
if (fileSizeMB < 2) {
chunkSize = 1024; // Small chunk size
} else if (fileSizeMB < 5) {
chunkSize = 4096; // Medium chunk size
} else if (fileSizeMB < 10) {
chunkSize = 8192; // Large chunk size
} else {
chunkSize = 16384; // Very large chunk size
}
// Create an array to hold the byte arrays
const byteArrays = [];
// Process the byte characters in chunks
let offset = 0;
// Loop through the byte characters and create Uint8Array chunks
while (offset < totalBytes) {
// Slice the byte characters into chunks
const slice = byteChars.slice(offset, offset + chunkSize);
// Convert the slice to a byte array
const byteNumbers = new Array(slice.length);
// Loop through the slice and convert each character to its char code
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
// Create a Uint8Array from the byte numbers
const byteArray = new Uint8Array(byteNumbers);
// Push the Uint8Array to the byteArrays array
byteArrays.push(byteArray);
// Update the progress
this.progress = Math.min(100, Math.floor((offset / totalBytes) * 100));
// Allow the UI to update
await new Promise(resolve => setTimeout(resolve, 0));
// Move the offset forward by the chunk size
offset += chunkSize;
}
// Set progress to 100% when done
this.progress = 100;
// Create and return a Blob from the byte arrays
return new Blob(byteArrays, { type: mimeType });
}
}
/**
* What it does:
Defines a nested FileWrapper class that holds:
fileName: Name of the file.
mimeType: MIME type like application/pdf or image/png.
base64Data: Encoded file content as a base64 string.
The method getFileAsBlob():
Fetches the most recent ContentDocumentLink for the given parentId.
Retrieves the latest ContentVersion for that ContentDocumentId.
Converts the binary data to a Base64 string.
Wraps it into a FileWrapper object and returns it to the frontend.
*/
public with sharing class FilesDownloaderCtrl {
public class FileWrapper {
@AuraEnabled public String fileName;
@AuraEnabled public String mimeType;
@AuraEnabled public String base64Data;
}
/**
* @description – This method retrieves the latest ContentDocumentLink for the given parentId.
* – It then retrieves the latest ContentVersion for that ContentDocumentId.
* @param parentId – The ID of the record for which attachments are to be fetched.
* @return {FileWrapper} – A FileWrapper object containing the file name, Base64-encoded data, and MIME type of the attachment.
*/
@AuraEnabled
public static FileWrapper getFileAsBlob(Id parentId) {
// Query the latest ContentDocumentLink for the given parentId
ContentDocumentLink cdl = [
SELECT ContentDocumentId
FROM ContentDocumentLink
WHERE LinkedEntityId = :parentId
ORDER BY ContentDocument.LatestPublishedVersionId DESC
LIMIT 1
];
// QUery the latest ContentVersion for the ContentDocumentId
ContentVersion cv = [
SELECT Title, VersionData, FileExtension, FileType
FROM ContentVersion
WHERE ContentDocumentId = :cdl.ContentDocumentId
ORDER BY VersionNumber DESC
LIMIT 1
];
// Create a FileWrapper object to return
FileWrapper fw = new FileWrapper();
fw.fileName = cv.Title + '.' + cv.FileExtension;
fw.mimeType = getMimeType(cv.FileExtension);
fw.base64Data = EncodingUtil.base64Encode(cv.VersionData);
return fw;
}
/**
* @description – Returns the MIME type based on the file extension.
* @param ext – The file extension (e.g., pdf, png).
* @return {String} – The corresponding MIME type or application/octet-stream if unknown.
*/
private static String getMimeType(String ext) {
Map<String, String> types = new Map<String, String>{
'pdf' => 'application/pdf',
'png' => 'image/png',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'csv' => 'text/csv',
'txt' => 'text/plain',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
};
return types.containsKey(ext.toLowerCase()) ? types.get(ext.toLowerCase()) : 'application/octet-stream';
}
}
When to Use This Approach:
  • You’re working with Files (ContentVersion / ContentDocument) instead of legacy Attachment objects.
  • You want to download files without using window-based redirects like window.open().
  • You need programmatic control in LWC (e.g., setting custom file names, MIME types, or conditionally allowing downloads).
  • Useful for scenarios in mobile apps, custom file viewer interfaces, or multi-file ZIP packaging logic (via base64 blobs).

🎯 Scenario 3: Downloading files by calling a REST API directly from LWC

In many real-world scenarios, Salesforce developers need to download files from external systems like Amazon S3 / Azure Blob etc. They can achieve this without Apex by using only the client-side fetch API in LWC. This use case shows how to trigger a file download directly from an LWC. The download is a Pdf file, executed using a REST API call.

Note – Lightning Web Security only supports some of the MIME Types. Text/csv is not supported. You need to use Apex for text/csv.

<template>
<lightning-card title="Download File From Rest Api Response" icon-name="utility:download">
<div class="slds-p-around_medium">
<template if:true={isLoading}>
<lightning-spinner alternative-text="Loading ZIP…" size="medium"></lightning-spinner>
</template>
<lightning-button
label="Download"
onclick={handleDownload}
disabled={isLoading}
></lightning-button>
</div>
</lightning-card>
</template>
/**
* What it does:
When the user clicks the Download button:
The component fetches a file from a mock API endpoint and triggers its download.
It uses the Fetch API to retrieve the file, converts it to a Blob, and then creates a temporary link to download the file.
Creates a temporary anchor (<a>) element to trigger the file download with a proper filename.
Cleans up the blob URL and DOM after the download is triggered.
*/
import { LightningElement } from 'lwc';
export default class FilesDownloader extends LightningElement {
isLoading = false;
/**
* @description – handleDownload method handles the download of a file from a mock API endpoint.
* It fetches the file, converts it to a Blob, and triggers the download process.
* @returns {Promise<void>} – Resolves when the download is complete or fails.
*/
async handleDownload() {
// Set loading state to true to show spinner
this.isLoading = true;
try {
// Fetch the file from the mock API endpoint
const response = await fetch('https://api.mockfly.dev/mocks/Your_Key/test-pdf&#39;);
// Get the response as a Blob
const blob = await response.blob();
// Create a URL for the Blob
const url = window.URL.createObjectURL(blob);
// Create a temporary anchor element to trigger the download
const link = document.createElement('a');
// Set the href
link.href = url;
// Set the download attribute with a filename
link.download = 'test-pdf';
// Trigger the download
link.click();
// Clean up the URL object
window.URL.revokeObjectURL(url);
} catch (error) {
// Handle any errors that occur during the download process
console.error('Download error:', error);
} finally {
// Ensure the loading state is reset regardless of success or failure
this.isLoading = false;
}
}
}

Leave a comment