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 !
Related
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));
I am scraping data from a URL containing a csv. The format of the data I'm scraping is like this:
I am doing this in Node.js and using the nodejs-requestify package: https://www.npmjs.com/package/requestify
When I console the response.getBody() it's in the exact same format as the screenshot provided.
I am trying to convert this to a JSON array that I can iterate over in a loop to insert the values into a database, however, am struggling to get the data into JSON format.
I've tried splitting the array in multiple ways (comma, single quote, double quote). I've tried JSON.parse() and JSON.stringify() (and both in combination).
Here is the code I'm using. Ultimately when I console.log rows in the loop, this is where it should be in JSON format, however, it's just coming in as comma separated values still.
requestify.get('URL').then(function(response) {
// Get the response body
var dataBody = response.getBody();
var lineArray = dataBody.split('\r\n');
var data = JSON.parse(JSON.stringify(lineArray));
for(var s = 0; s < data.length; s++) {
var rows = data[s];
console.log(rows)
}
});
There is a basic misundertanding I think
var lineArray = dataBody.split('\r\n');
lineArray now contains something like
"a", "b", "c"
but for doing something like
var data = JSON.parse(lineArray);
you need lineArray to be
{ "a":"1", "b":"2", "c":"3" }
I think you need something like
const lineData = lineArray.split(',');
const keys = ["name", "age", "gender"];
const jsonLineData = {};
keys.forEach((key, index) => {
jsonLineData[key] = lineData(index);
});
I solved this by using csvtojson and aws-dsk since my csv is hosted on S3.
async function startAWS(db){
//Retrieve AWS IAM credentials for the 'master' user
var awsCredentials;
try{
awsCredentials = await retrievePromise(config.get('aws'));
}
catch (e) {
console.log({error:e},'startAWS error');
}
//Setup the AWS config to access our S3 bucket
AWS.config = new AWS.Config({
accessKeyId : awsCredentials.principal,
secretAccessKey :awsCredentials.credential,
region:'us-east-1'
});
//Call S3 and specify bucket and file name
const S3 = new AWS.S3();
const params = {
Bucket: '***',
Key: '***' //filename
};
//Convert csv file to JSON
async function csvToJSON() {
// get csv file and create stream
const stream = S3.getObject(params).createReadStream();
// convert csv file (stream) to JSON format data
const json = await csv().fromStream(stream);
//connect to DB and continue script
db.getConnection()
.then(async (conn) => {
if(json.length) {
for(var s = 0; s < json.length; s++) {
var rows = json[s];
const insert = await conn.query(
'SQL HERE'
);
}
}
})
};
csvToJSON();
}
I have a problem in appending file object with form data,after appending file object to form data,form data is empty object.Please suggest
scope.uploadDigitalSignature = function (form) {
if (scope.digitalFiles.length) {
scope.docDetails.aliasName = scope.docDetails.aliasName;
scope.docDetails.digipassword = scope.docDetails.digipassword;
scope.docDetails.certificate = scope.docDetails.certificate;
var file = scope.digitalFiles[0];
var formData = new FormData(this);
formData.append('file', file);
// FileUpload.upload(scope.digitalFiles,docId,function(success,blobData){
var config = {
headers: {
'Content-Type': 'multipart/form-data',
}
};
var blobData = {
"file" : scope.docDetails.certificate
};
AdminService.uploadDigitalSignature(function(){
toastr.success('Signature uploaded successfully');
},docId,scope.docDetails.aliasName,scope.docDetails.digipassword ,blobData,config);
//}
//);
}
};
A FormData Object is an exotic object whose contents is not directly available to JavaScript.
To retrieve the contents of a FormData object, use its .entries() method:
var contents = Array.from(formData.entries());
console.log(contents);
For more information, see MDN Web API Reference - FormData.entries()
I try currently to recording data in my database. I use base64 npm module to translate my blob in base64 standards then store it into the database.
But when I sent my records to my database, the Get/* methods returns me exactly the same raw text for each records. Note that I have made the records on differents session, not in the same sent-stream :
Here the return of my database :
[{"_id":"5b09c5a9db6839382475442b","sound":{"type":"Buffer","data":[87,50,57,105,97,109,86,106,100,67,66,67,98,71,57,105,88,81,61,61]},"__v":0},{"_id":"5b09c5b7db6839382475442c","sound":{"type":"Buffer","data":[87,50,57,105,97,109,86,106,100,67,66,67,98,71,57,105,88,81,61,61]},"__v":0},{"_id":"5b09c5c4db6839382475442d","sound":{"type":"Buffer","data":[87,50,57,105,97,109,86,106,100,67,66,67,98,71,57,105,88,81,61,61]},"__v":0},{"_id":"5b09c69bdb6839382475442e","sound":{"type":"Buffer","data":[87,50,57,105,97,109,86,106,100,67,66,67,98,71,57,105,88,81,61,61]},"__v":0},
(... and so on)
I wonder what can produce this behavior.
I try currently to directly retrieve the data URL and retrieve the binary text directly from it as - pseudo code :
blob:http://localhost:[path]
-> then retrieve data in this path
-> then store in database data retrieved
Meanwhile, here my App.js :
class RecordingAPI extends React.Component {
constructor(props) {
super(props);
this.deleteAudio = this.deleteAudio.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
this.state = {
recording: false,
audios: [],
blob : {}
};
}
handleSubmit(e){
e.preventDefault();
Axios.post("/api/words",{
"sound":this.state.blob
})
//.then((res) => res.json())
.then((data) => console.log(data))
//pass submitted value to true in order to declench allDelete function
alert("Message sent, congratulation =)")
//this.state.deleteAll();
this.deleteAll();
}
async componentDidMount() {
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
// show it to user
this.audio.src = window.URL.createObjectURL(stream);
this.audio.play();
// init recording
this.mediaRecorder = new MediaRecorder(stream);
// init data storage for video chunks
this.chunks = [];
// listen for data from media recorder
this.deleteAll=this.deleteAll.bind(this) ;
this.mediaRecorder.ondataavailable = e => {
if (e.data && e.data.size > 0) {
this.chunks.push(e.data);
}
};
}
startRecording(e) {
e.preventDefault();
// wipe old data chunks
this.chunks = [];
// start recorder with 10ms buffer
this.mediaRecorder.start(10);
// say that we're recording
this.setState({recording: true});
}
stopRecording(e) {
e.preventDefault();
// stop the recorder
this.mediaRecorder.stop();
// say that we're not recording
this.setState({recording: false});
// save the video to memory
this.saveAudio();
}
saveAudio() {
// convert saved chunks to blob
const blob = new Blob(this.chunks, {type: audioType});
// generate video url from blob
const audioURL = window.URL.createObjectURL(blob);
console.log(audioURL);
// append videoURL to list of saved videos for rendering
const audios = this.state.audios.concat([audioURL]);
this.setState({audios});
var blob64 = Base64.encode(blob);
this.setState({blob : blob64})
}
Thanks.
I am making use of the pdfmake library for generating PDF documents in my node express application and want these to be sent straight back to the client to trigger the browser to automatically download the file.
As a reference point I have been using the following examples for my express middleware:
https://gist.github.com/w33ble/38c5e0220d491148de1c
https://github.com/bpampuch/pdfmake/issues/489
I have opted for sending a buffered response back, so the key part of my middleware looks like this:
function createPDFDocument(docDefinition, callback) {
var fontDescriptors = {
Roboto: {
normal: './src/server/fonts/Roboto-Regular.ttf',
bold: './src/server/fonts/Roboto-Medium.ttf',
italics: './src/server/fonts/Roboto-Italic.ttf',
bolditalics: './src/server/fonts/Roboto-MediumItalic.ttf'
}
};
var printer = new Printer(fontDescriptors);
var pdfDoc = printer.createPdfKitDocument(docDefinition);
// buffer the output
var chunks = [];
pdfDoc.on('data', function(chunk) {
chunks.push(chunk);
});
pdfDoc.on('end', function() {
var result = Buffer.concat(chunks);
callback(result);
});
pdfDoc.on('error', callback);
// close the stream
pdfDoc.end();
}
In my angular application I am using the $resource service and have an endpoint defined like so:
this.resource = $resource('api/document-requests/',
null,
<any>{
'save': {
method: 'POST',
responseType: 'arraybuffer'
}
});
When I try this out, I dont get any browser download kicking in, the response I receive is as follows when looking in Chrome:
And the response headers are as follows:
So it seems I'm not a million miles off, I have searched around and found solutions mentioning about converting to Blob, but I think that's only relevant if I were serving back a Base64 encoded string of the document.
Can anyone suggest what may be my issue here?
Thanks
Here's a router:
router.get('/get-pdf-doc', async (req, res, next)=>{ try {
var binaryResult = await createPdf();
res.contentType('application/pdf').send(binaryResult);
} catch(err){
saveError(err);
res.send('<h2>There was an error displaying the PDF document.
'</h2>Error message: ' + err.message);
}});
And here's a function to return the pdf.
const PdfPrinter = require('pdfmake');
const Promise = require("bluebird");
createPdf = async ()=>{
var fonts = {
Helvetica: {
normal: 'Helvetica',
bold: 'Helvetica-Bold',
italics: 'Helvetica-Oblique',
bolditalics: 'Helvetica-BoldOblique'
};
var printer = new PdfPrinter(fonts);
var docDefinition = {
content: [
'First paragraph',
'Another paragraph, this time a little bit longer to make sure,'+
' this line will be divided into at least two lines'
],
defaultStyle: {
font: 'Helvetica'
}
};
var pdfDoc = printer.createPdfKitDocument(docDefinition);
return new Promise((resolve, reject) =>{ try {
var chunks = [];
pdfDoc.on('data', chunk => chunks.push(chunk));
pdfDoc.on('end', () => resolve(Buffer.concat(chunks)));
pdfDoc.end();
} catch(err) {
reject(err);
}});
};
Everything seems fine to me, the only thing missing is the logic to trigger the download.
Check out this CodePen as an example.
Here I'm using base64 encoded data, but you can just use binary data as well, just don't forget to change the href, where I'm mentioning scope.dataURL = base64....
I had issue serving PDF files from Node.js as well, so I made use of phantomjs. You can checkout this repository for full codebase and implementation.
console.log('Loading web page')
const page = require('webpage').create()
const args = require('system').args
const url = 'www.google.com'
page.viewportSize = { width: 1024, height: 768 }
page.clipRect = { top: 0, left: 0 }
page.open(url, function(status) {
console.log('Page loaded')
setTimeout(function() {
page.render('docs/' + args[1] + '.pdf')
console.log('Page rendered')
phantom.exit()
}, 10000)
})