I read the file in Django side as a python file:
with open("file.docx", mode="rb") as f:
bytes = f.read()
and I send it:
from django.http import FileResponse
return FileResponse(bytes)
In javascript I use this function:
function downloadBuffer(arrayBuffer, fileName) {
const a = document.createElement('a')
a.href = URL.createObjectURL(new Blob(
[ arrayBuffer ],
{ type: 'aapplication/vnd.openxmlformats-officedocument.wordprocessingml.document' }
))
a.download = fileName
a.click()
}
However the file is corrupted. How can I get the file correctly?
Related
I am using Express.js with Typescript and I would like to send a UInt8Array as binary data.
This is what I use so far and it works, but I would like not to save the file before, because I think it wastes performance:
const filePath = path.resolve(__dirname, 'template.docx');
const template = fs.readFileSync(filePath);
const buffer: Uint8Array = await createReport({
template,
data: {
productCode: data.productCode,
},
});
fs.writeFileSync(path.resolve(__dirname, 'output.docx'), buffer);
res.sendFile(path.resolve(__dirname, 'output.docx'));
I am using docx-templates to generate the file by the way.
You can use a PassThrough stream for this purpose, it'll keep the file in memory with no need to write to disk.
Something like this should do it:
const stream = require("stream");
const readStream = new stream.PassThrough();
// Pass your output.docx buffer to this
readStream.end(buffer);
res.set("Content-disposition", 'attachment; filename=' + "output.docx");
res.set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
readStream.pipe(res);
The complete node.js code:
const fs = require("fs");
const express = require("express");
const port = 8000;
const app = express();
const stream = require("stream");
app.get('/download-file', (req, res) => {
const buffer = fs.readFileSync("./test.docx");
console.log("/download-file: Buffer length:", buffer.length);
const readStream = new stream.PassThrough();
readStream.end(buffer);
res.set("Content-disposition", 'attachment; filename=' + "test.docx");
res.set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
readStream.pipe(res);
});
app.listen(port);
console.log(`Serving at http://localhost:${port}`);
To test, add a 'test.docx' file to the same directory, then point your browser to http://localhost:8000/download-file
Terry,
Thanks for the update of your answer and providing the full code. However, it still does not help much. I am trying to understand how I can handle this on the front-end side, in my case in Vue. Here is the following code:
router.post('/chart/word', async (req, res, next) => {
try {
if (!req.body.chartImage) throw new BadRequest('Missing the chart image from the request body')
const wordTemplate = await s3GetFile('folder', 'chart-templates-export/charts-template.docx')
const template = wordTemplate.Body
const buffer = await createReport({
cmdDelimiter: ["{", "}"],
template,
additionalJsContext: {
chart: () => {
const dataUrl = req.body.chartImage.src
const data = dataUrl.slice("data:image/jpeg;base64,".length);
return { width: 18 , height: 12, data, extension: '.jpeg' }
}
}
})
const stream = require('stream')
const readStream = new stream.PassThrough()
readStream.end(buffer)
res.set("Content-disposition", 'attachment; filename=' + "output.docx")
res.set("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
readStream.pipe(res)
} catch (err) {
console.log(err)
next(err)
}
})
And here is my Vue code, tested various stuff, but nothing...:
async exportCharts() {
console.log('this.$refs.test: ', this.$refs.test)
let img = {
src: this.$refs.test.getDataURL({
type: 'jpeg',
pixelRatio: window.devicePixelRatio || 1,
backgroundColor: '#fff'
}),
width: this.$refs.test.getWidth(),
height: this.$refs.test.getHeight()
}
const answersReq = await this.axios({
method: 'post',
url: '/pollAnswers/chart/word',
data: {
chartImage: img
}
responseType: 'arraybuffer' // 'blob' // 'document'
})
console.log('answersReq: ', answersReq)
if (answersReq.data) {
downloadURL(answersReq.data, 'report.docx')
}
}
What I am basically doing is: sending an image to the API (taken from html vue-echart element), then inserting it in a docx template, by using docx-templates library, which returns me Uint8Array that I want to export as the new Word Document with the populated charts. Then, the user (on the UI) should be able to choose the destination.
Here is the code for the download URL:
export function downloadURL(data, fileName) {
const mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
const blob = new Blob([data], { type: mimeType })
const url = URL.createObjectURL(blob)
const element = document.createElement('a')
element.href = url
element.download = fileName
element.style.display = 'none'
document.body.appendChild(element)
element.click()
URL.revokeObjectURL(element.href)
document.body.removeChild(element)
}
P.S. Just to mention, if I directly save the buffer (the Uint8Array returned from the createReport) in the API, it works, the file is downloaded successfully and I can read it without any problems - it populates the correct chart in the file.
UPDATE:
I figured that out, but I am not sure why this is necessary and why it works that way and not the other. So, in the /chart/word endpoint, I am converting the Uint8Array buffer into a stream, then passing it as a response (the same way you used). Afterwards, in the Vue, I fetched this as responseType: 'arraybuffer', which converted the stream response into Uint8Array buffer again, then, I used the same method for the download and it works. Initially, I tried to send directly the buffer (without converting it as stream as you mentioned), but then on the front-end, the response was received as object that contained the Uint8Array buffer values, which was not what is expected and I could not create legit docx file. So, for some reason, it is required to convert the buffer as stream in the API, before sending it as response. Afterwards, on the front-end, I have to convert it back to arraybuffer and, finally, to make the docx download.
If you can explain to me why it works like that, I will be very happy.
I need to axios to get a pdf buffer from nodejs and more some data info.
If I set the axios post with responseType: "arraybuffer", and I make node send only the pdf buffer it is working.
But I need to return some more data info from node and if I get rid off the "responseType:arrayBuffer", in order to receive a json, I am unable to convert pdfBuffer to pdf with new Blob function.
I get BLOB invalid.
What am I doing wrong?
This is not working:
//front end:
const resp=await axios.post("atestadosClientes/generatePDF", data);
const file = new Blob([response.data.pdfBuffer], {
type: "application/pdf",
});
//Build a URL from the file
const fileURL = URL.createObjectURL(file);
//Node response:generate pdf buffer
return res.send({
message: "Success",
id: resp[0].insertId,
pdfBuffer: pdfSignedBuffer, //pdf buffer
});
This is working:
//front end:
const resp=await axios.post("atestadosClientes/generatePDF", data, {
responseType: "arraybuffer",
});
},
const file = new Blob([response.data], {
type: "application/pdf",
});
//Build a URL from the file
const fileURL = URL.createObjectURL(file);
//Node response:generate pdf buffer
return res.send(pdfBuffer)
I'm new to python flask, I'm using flask in backend and react js in front end and pymongo for the database. I want to upload the file from ReactJs to the flask server, I 'm getting an error while doing second method, How can I do it. Below is the code that I have tried.
I had tried two examples one is working and the other is not I don't know why.
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'file'
Directly sending the file to API. Case 1
Python code
import os
from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = set([ 'png', 'jpg', 'jpeg'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
#app.route('/api/users/add_photo', methods=['POST', "GET"])
def upload_file():
if request.method == 'POST':
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# if user does not select file, browser also
# submit a empty part without filename
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
image = upload(open(filename, 'rb'))
user_id = user.insert({"file":image})
return jsonify({'result': "file Upload successfully!!!"})
onSubmitImage(e) {
let file = this.state.file;
let formData = new FormData();
formData.append("file", file);
this.props.imageUpload(formData);
}
Above example is working perfectly
Sending a file to API in the object. Case 2
Python code
import os
from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = set([ 'png', 'jpg', 'jpeg'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
#app.route('/api/users/add_photo', methods=['POST', "GET"])
def upload_file():
if request.method == 'POST':
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
name = request.get_json(force=True)["name"]
last_name = request.get_json(force=True)["last_name"]
email = request.get_json(force=True)["email"]
# if user does not select file, browser also
# submit a empty part without filename
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
image = upload(open(filename, 'rb'))
user_id = user.insert({"file":image,"name":name,"last_name":last_name,"email":email})
return jsonify({'result': "file Upload successfully!!!"})
onSubmitImage(e) {
let file = this.state.file;
let formData = new FormData();
formData.append("file", file);
const data = {
file: formData
name: this.state.name
last_name: this.state.last_name
email: this.state.last_name
};
this.props.imageUpload(data);
}
I don't know why first is working and second not. I want to implement the second example because there other are data like name, last_name, email with image file.
You need to add your data to your FormData()- ie
let file = this.state.file;
let formData = new FormData();
formData.append("file", file);
formData.append("name", this.state.name);
formData.append("last_name", this.state.last_name)
formData.append("email", this.state.last_name)
this.props.imageUpload(formData);
I am sending a pdf file from express server like this -
app.get("/pdf", (req, res) => {
var file = fs.createReadStream('./public/file.pdf');
res.contentType("application/pdf");
res.send(file);
})
I want to display it on react side (Note- display pdf , not download)
I tried converting it into blob but it shows blank pdf
I am calling below function on click in react end
viewHandler = async () => {
axios.get('http://localhost:4000/pdf')
.then(response => {
console.log(response.data);
//Create a Blob from the PDF Stream
const file = new Blob(
[response.data],
{ type: 'application/pdf' });
//Build a URL from the file
const fileURL = URL.createObjectURL(file);
//Open the URL on new Window
console.log("Asdasd");
window.open(fileURL);
})
.catch(error => {
console.log(error);
});
};
I want to display pdf without using static url to server pdf file
like -
<object data='http://localhost:4000/file.pdf'
type='application/pdf'
width='100%'
height='700px' />
I'm developing a chatbot using Microsoft Bot Framework with Node Js.
My purpose is to send the user a csv file while he asks me something.
I've implemented the following function but while I download the file 'prova.csv' the format is not recognized.
Inspecting well the format in output is of the type: "data"
Anyone can help me what's wrong? Thanks
function (session, results, next) {
if (results.response.entity === 'Si') {
const contentType = 'text/csv';
const response = session.dialogData.far_ric_formato.response.rows;
const csv = response.map(ric => `${ric.num};${ric.pin}`).join('\n');
session.send({
text: 'Ecco il CSV pronto per il download (MOCK)',
attachments: [
{
contentType: contentType,
contentUrl: `data:${contentType};base64,${Buffer.from(csv).toString('base64')}`,
name: 'prova.csv'
}
]
});
Base64 string of a .csv file can't be directly rendered on client side, as a workaround you can for example code like this:
var restify = require('restify');
var fs = require('fs');
bot.dialog('download', (session, result)=>{
fs.readFile('./files/test.csv', function(err, data){
var contentType = 'text/csv';
var base64 = Buffer.from(data).toString('base64');
var msg = new builder.Message(session)
.addAttachment({
contentUrl: 'http://localhost:3978/csv/'+base64, //replace with your server url + base64 string.
contentType: contentType,
name: 'MyTest.csv',
});
session.send(msg);
});
}).triggerAction({matches:/^download/i});
server.get('/csv/:base64code', (req, res, next)=>{
let base64code = req.params.base64code;
res.header('Content-disposition', 'inline; filename=test.csv');
res.header('Content-type', 'application/csv');
res.send(Buffer.from(base64code, 'base64'));
});
When user trigger the download dialog, it will send this file as attachment and when user click on this file, this .csv file will be downloaded in user's client side.