I am using this approach for downloading a pdf file from server (laravel 8 (api sanctum) + vue 3)
In the vue component I have this function that downloads the file
const onDownloadDocument = (id) => {
axios.post('/api/document/download', {id: id},{
responseType: 'blob'
}).then(response => {
let filename = response.headers['content-disposition'].split('filename=')[1]
dLink.value.href = window.URL.createObjectURL(response.data)
dLink.value.setAttribute('download',filename)
dLink.value.click()
}).catch(error => {
console.log(error)
})
where dLink is a link ref
const dLink = ref(null)
in template:
<a ref="dLink"/>
It works this approach until today.... after I updated the project (composer update and npm update)
Now when click to download the file (call the onDownloadDocument function) I get an error:
contract.js:1049 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'responseType')
Any idea why ?
The api backend return the file blob
return Storage::download($document->filename);
First you need to create a blob and put your response in it,
And as I said in my comment you don't need to attach to a real anchor tag, you can just create an element, attach it to the body, simulate the click and remove it immediately
const blob = new Blob([response], {type: 'application/pdf'})
if (window.navigator['msSaveOrOpenBlob']) {
window.navigator['msSaveBlob'](blob, filename)
}
else {
const elem = window.document.createElement('a')
elem.href = window.URL.createObjectURL(blob)
elem.download = filename
document.body.appendChild(elem)
elem.click()
document.body.removeChild(elem)
}
This worked for me, specify to axios that you are expecting a blog, and that's it:
axios({
url: 'http://localhost:5000/endpoint?',
method: 'GET',
responseType: 'blob', // <= important
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf');
document.body.appendChild(link);
link.click();
});
Related
I'm trying the below code to open pdf for preview.
previewPdf() {
let mypayload;
mypayload = {
id: ...,
status: ...
};
this.service.previewPdf(mypayload).subscribe(
(response) => {
const blob = new Blob([response], { type: 'application/pdf' });
const file_name = 'xyz.pdf';
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, file_name);
} else {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
window.open(link.href, '_blank')
}
}
);
}
previewPdf API code is already implemented in the service file and the PDF is being generated at Flask backend and sent to angular.
The above code works fine. It opens pdf in a new window but the problem is, the pdf name is always some random blob code.
How can I put a filename of my choice (eg.. xyz.pdf) instead of some code?
I run into unknown issue as described below,
I have node/express api which returns data as shown below,
...
...
const zip = new AdmZip();
// Define zip file name
const timedate = this.getDateTime();
let downloadName = `myZip-${timedate}.zip`;
const data = zip.toBuffer();
const fileType = 'application/zip';
res.writeHead(200, {
'Content-Disposition': `attachment; filename="${downloadName}"`,
'Content-Type': fileType,
})
return res.end(data);
ReactApp
const ReadResponse = async (response: any) => {
const blob = await response.blob();
/**
I can confirm above blob object contains data as shown below
size:13258
type:"application/zip"
[[Prototype]]:Blob
**/
const a = document.createElement('a');
document.body.appendChild(a);
const url = window.URL.createObjectURL(blob);
a.href = url;
const filename = "myZip.zip";
a.download = filename;
a.click();
setTimeout(() => {
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}, 0)
};
hitting localhost:2000/api/download, it downloads the zip folder and when I open it I can see the content in it.
But the same code when goes to lambda function, hitting whateverdomain/api/download api returns blob response, I can download the zip folder as well but when try to open it, it throws
windows can't open the folder
Tried with different different approaches but can't seem to find reason what's wrong ?
I have multiple url's, pointing to different files. I want to be able to download them just by using the url string automatically with Javascript code, instead of manually going to the link and downloading them.
I have searched a lot of other answers on stackoverflow, few suggest creating an anchor tag in document body, but I am doing everything on backend not creating an index.html
edit: I am using next where for an api end point I am getting post requests, each which contains URL for a file which I have to download.
This is how I expect my POST request to come:
I want to be able to do something like this (in nextjs):
export default async function handler (req, res) {
if(req.method === "POST") {
let path = "./downloads/file"
await download(req.body.url, path)
}
}
Is the download function possible, if so please help me with the implementation or provide any helpful packages to do the same.
You can use HTTP clients like Axios. It makes it easy to send async HTTP requests to REST endpoints and perform CRUD operations.
You can refer to the snippet below that I have used in my previous projects for file downloads. I guess this is what you are looking for:
const fs = require('fs')
const Path = require('path')
const axios = require('axios')
const crypto = require('crypto')
async function downloadFile(url) {
const uuid = crypto.randomUUID()
const path = Path.resolve("./utils", "uploads", uuid)
const writer = fs.createWriteStream(path)
const response = await axios({
url,
method: 'GET',
responseType: 'stream'
})
response.data.pipe(writer)
return new Promise((resolve, reject) => {
writer.on('error', reject)
response.data.on('end', () => {
resolve(uuid)
})
})
}
Hope this helps, and don't forget to check out their documentation. Here is the
link.
Download by get request
const downloadFabricFormat = async () => {
try{
await axios({
url: '/api/fabric/fabric_excel_format/',
method: 'GET',
responseType: 'blob',
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Fabric Excel Format.xlsx');
document.body.appendChild(link);
link.click();
});
} catch(error){
console.log(error)
}
};
Download by post request
const postFabricQRCode = async (values) => {
try{
await axios({
url: `/api/qr/code/download/`,
method: 'POST',
responseType: 'blob',
data: values,
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'fabric-qr-code.pdf');
document.body.appendChild(link);
link.click();
});
} catch(error){
console.log(error)
}
};
Change file type here. link.setAttribute('download', 'Fabric Excel Format.xlsx');
Also manage your back-end yourself.
So the API's response contains data property which should contain the .zip file that i need. Its written in a format i do not understand.
The format:
I tried using .blob() as referenced in similar questions here on Stackoverflow, but it doesn't seem to work.
The ideal solution is this: when client presses the button, he should be prompted to download said .zip file (the one from the HTTP response) locally. I'm using axios and the request type is PUT.
My code example so far:
const exportCards = () => {
axios
.put(url, {
ids: ids,
})
.then((res) => {
return res.data.blob();
})
.then((blob) => {
var file = window.URL.createObjectURL(blob);
window.location.assign(file);
})
.catch((e) => console.log(e));
};
What I wanted:
send files of any formats from the back-end to the front-end
My tools:
axios, express, saveAs
The problem I faced with:
Unable to download zip file using axios
https://github.com/eligrey/FileSaver.js/issues/156
https://github.com/axios/axios/issues/448
Nothing helped me, probably because I did something wrong. But here is a simple and quick solution that I came up with:
//BE
const filename = "my-file-name.json";
const zip = new AdmZip();
zip.addFile(filename, body);
const content = zip.toBuffer();
res.set({
"Content-Length": Buffer.byteLength(content), //I'm not sure if this is necessary, but it's better to let it be :-)
"Content-Type": "text/plain",
"Content-Disposition": `attachment; filename=${filename}.${format}`,
});
res.status(200).send(content.toString("hex")); //my solution to the problem
//FE
const { headers, data } = await axios.post(myEndpoint);
const headerLine = headers["content-disposition"];
const filename = headerLine.replace(/[\w; ]+filename=/g, "");
const content = Buffer.from(data, "hex");
const blob = new Blob([content], { type: "application/zip" });
saveAs(blob, filename); //file-saver npm package
a tag has download attribute, in .then you can try something like that
const url = new Blob([response.data],{type:'application/zip'});
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip'); //set download attribute to link
document.body.appendChild(link);
link.click(); // this will download file.zip
I would suggest fetch over axios in concern of zip file because sometimes the response in axios request is not accurate and you might fall into currupted zip file while downloading, the best might be using a package called file-saver and fetch. I am posting this answer to help the developers to grab the concept only, the code is tested in React.
package file-saver:https://www.npmjs.com/package/file-saver
Now make any function according to your choice in react, I am assuming functional component so will write method according to functional component syntax.
note before using saveAs function you need to import from the installed package file-saver.
import { saveAs } from 'file-saver';
const downloadZipFileFromLaravel=()=>{
fetch(`your url`)
.then(res => res.blob())
.then(blob => saveAs(blob, 'Auto Photos.zip')) // saveAs is a function from the file-saver package.
.catch((err) => {
console.log(err.message);
});
}
at the end you need to connect the function with a button with onClick.
example
<button onClick={()=>downloadZipFileFromLaravel()}> </button>
Note: usage of file saver in pure javascript, you can check this:
How to use filesaver.js
For more information you can see the below discussion:
Reference: https://github.com/eligrey/FileSaver.js/issues/156
Your problem is that you didn't explicitly specify the response type in your PUT request. This should work:
const exportCards = () => {
axios
.put(url, {
ids: ids,
}, {
responseType: 'blob'
})
.then((res) => { // Now 'res.data' is Blob, not a string
var file = window.URL.createObjectURL(res.data);
window.location.assign(file);
})
.catch((e) => console.log(e));
};
i have a bunch of VHD files stored on a private Server, which are accessible through a url.
I am trying upload these vhd files directly to my azure storage account using the azure javascript npm libraries. The vhds have to be uploaded as page-blobs. I tried using the method uploadPagesFromURL() of the pageblobClient but with no success. My code looks roughly like this:
async function uploadVHD(accessToken, srcUrl)
{
try {
// Get credentials from accessToken
const creds = new StorageSharedKeyCredential(storageAccount.name, storageAccount.key);
// Get blobServiceClient
const blobServiceClient = new BlobServiceClient(`https://${storageAccount.name}.blob.core.windows.net`, creds);
// Create Container
const containerClient = blobServiceClient.getContainerClient("vhd-images");
await containerClient.createIfNotExists();
const src = srcUrl.replace('https://', 'https://username:password#');
// Upload to blob storage
const pageBlobClient = containerClient.getPageBlobClient("Test.vhd");
// Get fileSize of vhd
const fileSize = (await axiosRequest(src, { method: "HEAD" })).headers["content-length"];
const uploadResponse = await pageBlobClient.uploadPagesFromURL(src, 0, 0, fileSize);
return uploadResponse;
} catch (error) {
return error;
}
});
It is not possible to upload the Page Blob with your URL directly. You need to read data from the url. Then upload using uploadPages method.
axios.get(URL, {
responseType: 'arraybuffer'
})
.then((response) => {
console.log(response.data)
console.log(response.data.length)
// upload page blob...
}).catch((error) => {
//handle error
});
// uploadPages method
const uploadResponse = pageBlobClient.uploadPages(data, 0, dataLength);