I got this data from backend when try to get a pdf file:
`%PDF-1.7 %���� 5 0 obj <</Filter/FlateDecode/Length 823>>stream x���MS�0���{l���)&���#CCK'!%�ӿߕmb���;�y�Ҿ��K��H�����aN��q��%�Iz&#�i�T
<......>
1950
%EOF\`
How can REACT read and open this as pdf file in a new tab?
NOTE: I'm able to see the PDF file content in postman when call backend endpoint.
I tried this:
Backend controller (Nestjs):
#Get('/getPDF/:uuid')
async getFile(
#Param('uuid') uuid: string,
#Response({ passthrough: true }) res,
): Promise<StreamableFile> {
const resp = await this.service.downloadPDF(uuid);
if (!resp) {
return null;
}
res.header('Content-Type', `application/pdf`);
res.header('Content-Disposition', `attachment; filename="${resp.fileName}`);
return new StreamableFile(resp.buffer); // resp.buffer === Uint8Array
}
Frontend (REACT):
This will call backend api to get pdf file:
getPDF(uuid: string): Promise<AxiosResponse<Blob>> {
return this.httpClient.get(`${this.apiUrlPath}/getPDF/${uuid}`, {
responseType: 'blob',
});
}
This was supposed to render the pdf file
const response = await api.getPDF(uuid);
window.open(URL.createObjectURL(response.data));
I got this error:
TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.
UPDATED
Change AxiosResponse type from Blob to ArrayBuffer and create a new Blob from that buffer, solves the issue
This works:
getPDF(uuid: string): Promise<AxiosResponse<ArrayBuffer>> {
return this.httpClient.get(`${this.apiUrlPath}/getPDF/${uuid}`, {
responseType: 'arraybuffer',
});
}
const response = await api.getPDF(uuid);
const blob = new Blob([response.data], { type: "application/pdf" });
window.open(URL.createObjectURL(blob));
Thanks amir sarfar
Try passing a blob to createObjectURL:
const response = await api.getPDF(uuid);
const blob = new Blob([response.data], { type: "application/pdf" });
window.open(URL.createObjectURL(blob));
Related
Thanks in advance for taking a look. I am working on being able to download a zip file from react through a django api request. I am able to click on my pop up that downloads the zip file, but when I double click on the zip file to open, I get this error: "Unable to expand file_name.zip. It is an unsupported format" My response with the zip file seems to be passing correctly to the front end, so I am thinking it may be something wrong with the react code when making the "blob"? Thanks again.
Django code:
class DownloadZip(APIView):
def post(self, request, format=None):
# information to find file to create zip
profile_name = request.data["profile"]
profile_year = request.data["year"]
# file path to create zips from
path = str(Path(__file__).parent.resolve())
zip_dir = shutil.make_archive(profile_name + profile_year, "zip", path + "/" + profile_name + profile_year)
s = io.StringIO(zip_dir)
response = HttpResponse(s, content_type = "application/zip")
zip_name = profile_name + profile_year + ".zip"
response["Content-Disposition"] = f"attachment; filename={zip_name}"
return response
React code:
downloadZip = async () => {
const params = {
profile: this.state.profileName,
year: this.state.year,
};
axios({
url: `${serverUrl}/download_zip`,
method: "post",
data: params
}).then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data],{type:'application/zip'}));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
},
(error) => {
console.log(error);
});
}
I did do a fellow commentor's suggestion, and updated to get route with query params, but am having the same issue. I can double click on the zip link on the web browser but a pop up appears "Unable to expand filename.zip. It is an unsupported format"
Please try adding {responseType: 'arraybuffer'}. I also had the same problem but after adding this {responseType: 'arraybuffer'}. I am getting correct file.
downloadZip = async () => {
const params = {
profile: this.state.profileName,
year: this.state.year,
};
axios.post(
`${serverUrl}/download_zip`,
params,
{
responseType: 'arraybuffer'
}
).then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data],{type:'application/zip'}));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
},
(error) => {
console.log(error);
});
}
I am able to use sObject to put an Attachment onto one of my records. The problem is that SF is not recognizing the file as a PDF but as a generic file.
const base64data = await new Buffer.from(pdfBuffer).toString('base64');
try {
await conn.sobject('Attachment').create({
ParentId: filename,
Name: resumeFileName,
Body: base64data,
ContentType: fileType,
Description: 'Resume Attachment',
});
} catch (e) {
console.log('Attachment Error', e);
}
When I look at the attachments of my record, the file does not have all of the options that a PDF file has (only download and delete)
Thanks in advance!
Turns out in order for Salesforce to recognize the pdf correctly you need to have the content type set to application/pdf AND the name of the file must include the .pdf extension. This worked for me:
(async () => {
const jsforce = require('jsforce');
const fs = require('fs');
var conn = new jsforce.Connection({
instanceUrl : '...',
accessToken : '...'
});
const pdfData = fs.readFileSync('./test.pdf').toString('base64');
try {
await conn.sobject('Attachment').create({
ParentId: '0012300000RWedX',
Name: 'My Test PDF.pdf', // <= Turns out the name has to have .pdf
Body: pdfData,
ContentType: 'application/pdf',
Description: 'Testing PDF Attachment',
});
} catch(err) {
console.error(err);
}
})();
I'm trying to download (using FileSaver.saveAs) a byte array getting from the server (nodejs\express) as a zip. I'm manage to download it as zip, but it doesn't open (it is invalid).
I had few miss-clarities regarding the way the data should be defined - content type, responseType both in the server and in the client, should I convert it to a blob, etc) - but I think I over-come them with the detailed below code.
The problem is at the final function, in the exportAsZip function - the data reaches there in the right size, but converting it to a Blob inflate it and probably corrupt it.
Here is my code (server side - node.js-express using middleware function):
THIS IS ALREADY UPDATED AS FIXED CODE:
The router is express-router:
router.use(<some route>,(req, res, next) => {
return getData(req.query).then((dataResult) =>
{
{
res.contentType('application/zip');
return res.send([dataResult]); //Data result is byte array
}).catch((err) => {
console.error(err);
});
});
In the client side (angular):
This is the component function:
downloadAsZip()
{
let fileName : string = <fileName>;
this.srv.getData().subscribe(result =>
{
// const blob = new Blob([result], { type: 'application/octet-stream' })
/*This is the needed fix:*/
const byteArray = new Uint8Array(result);
const blob = new Blob([byteArray]);
this.fileService.exportAsZip(blob, fileName);
},
error => console.log(error)
);
}
This is the srv.getData code:
getData() : Observable<any>
{
return this.http.get(<path>, /*{ responseType: 'blob' } - not needed*/)
}
This is the fileService function (exportAsZip):
exportAsZip(data, fileName)
{
/*The data is with a correct size, but converting it to a blob object inflate its size*/
/*this should be removed also*/
//const blobData: Blob = new Blob([data], {type: 'application/zip'});
FileSaver.saveAs(/*blobD*/data, fileName + '.zip');
}
Fixed the problem - The main change is to convert the byte Array data to Uint8Array and then create a blob which will be saved using the FileSaver.saveAs.
Also, removed the { responseType: 'blob' } from the get request header.
The above code is now fixed !
I have a complex object parameter that I need to send as post, as it could be too long for querystring. The post call is asking to have an excel file dynamically generated and then downloaded asynchronously. But all of this is happening inside of a react application. How does one do this using axios.post, react, and webapi? I have confirmed that the file does generate and the download up to the response does come back, but I'm not sure how to actually open the file. I have a hidden iframe that I'm trying to set the path, src, of the file to, but I dont know what response property to use.
// webapi
[HttpPost]
public HttpResponseMessage Post([FromBody]ExcelExportModel pModel)
{
var lFile = ProductDataModel.GetHoldingsExport(pModel);
var lResult = new HttpResponseMessage(HttpStatusCode.OK);
lResult.Content = new ByteArrayContent(lFile);
lResult.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "HoldingsGridExport.xls"
};
lResult.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return lResult;
}
// client side api
static getHoldingsExport({ UserConfigurationID, UserID, Configurations, ViewName, SortModel, FilterModel, UserConfigType, IsDefault, LastPortfolioSearchID = null, ProductId }) {
const filterModel = JSON.stringify(FilterModel); // saving as string as this model is dynamically generated by grid out of my control
const sortModel = JSON.stringify(SortModel);
let params = JSON.stringify({
UserConfigurationID,
UserID,
Configurations,
ViewName,
filterModel,
sortModel,
UserConfigType,
IsDefault,
LastPortfolioSearchID,
ProductId
});
return axiosInstance.post("/api/HoldingsExport", params);
}
// client side app call to get file
HoldingsApi.getHoldingsExport(config)
.then(function(response) {
debugger;
let test = response;
})
.catch(error => {
toastr.success('Failed to get export.');
});
This is how I've achieved file downloads by POSTing via Axios:
Axios.post("YOUR API URI", {
// include your additional POSTed data here
responseType: "blob"
}).then((response) => {
let blob = new Blob([response.data], { type: extractContentType(response) }),
downloadUrl = window.URL.createObjectURL(blob),
filename = "",
disposition = response.headers["content-disposition"];
if (disposition && disposition.indexOf("attachment") !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, "");
}
}
let a = document.createElement("a");
if (typeof a.download === "undefined") {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
}).catch((error) => {
// ...
});
Just in case the above solution does not serve you quite well, here is how I could be able to download videos that are hosted on S3 AWS buckets,
const handleDownload = () => {
const link = document.createElement("a");
link.target = "_blank";
link.download = "YOUR_FILE_NAME"
axios
.get(url, {
responseType: "blob",
})
.then((res) => {
link.href = URL.createObjectURL(
new Blob([res.data], { type: "video/mp4" })
);
link.click();
});
};
And I trigger handleDownload function in a button with onClick.
The url in the function has the video URL from S3 buckets
My Angular 1 application saves files to S3 and allows for a wide variety of files types.
When I retrieve the objects I use the following code:
export function show(req, res) {
const s3 = new aws.S3();
const s3Params = {
Bucket: S3_BUCKET,
Key: req.query.key + ''
};
res.attachment(req.query.key + '');
var fileStream = s3.getObject(s3Params).createReadStream();
fileStream.pipe(res);
}
I would like to open the received file on the client in a new window (just like on the AWS console) but I can't figure out how to go about it.
For example on the client side does not work at all:
.then(
(data) => {
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
)
I really don't understand how the concept of data streams works.
If you don't have to download pdf, you may open it directly from s3.
s3client.getResourceUrl("your-bucket", "some-path/some-key.jpg");
This will return you url to the file.
So you need code like:
export function show(req, res) {
this.s3client = new aws.S3({
accessKeyId: options.accessKeyId,
secretAccessKey: options.secretAccessKey,
region: options.region
})
let resourceUrl = s3client.getResourceUrl(S3_BUCKET, req.query.key + '');
window.open(resourceUrl, '_blank');
}
I'm sorry, can't test it right now, but try. Should work.
All I had to do was get a signedUrl for the resource for this to work much simpler than what I was trying to do.
export function show(req, res) {
const s3 = new aws.S3();
const s3Params = {
Bucket: S3_BUCKET,
Key: req.query.key + ''
};
s3.getSignedUrl('getObject', s3Params, (err, data) => {
if (err) {
console.log(err);
return res.end();
}
const returnData = {
signedRequest: data,
};
res.write(JSON.stringify(returnData));
res.end();
});
}
and on the client all I have to do is open the link in a new tab:
openDoc(doc) {
this.$http()
.then(
(data) => {
this.$window.open(data.data.signedRequest, '_blank')
}
)
.catch(
(err) => {
this.Notification.error('failed to download attachment');
}
)
}