PDFMake has this method for streaming a generated PDF document :
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
pdfDoc.pipe(fs.createWriteStream('document.pdf')); // send to output stream
pdfDoc.end();
And fastify has this to stream things :
fastify.get('/streams', function (request, reply) {
const fs = require('fs')
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
reply.send(stream) // send from input stream
})
How are those two working together? Preferably, PDFMake should directly stream through fastify and not buffer the entire PDF before sending it.
For reference, with Koa (or Express), I would simply do :
app.use(async (ctx, next) => {
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
res.setHeader('content-type', 'application/pdf');
res.setHeader('content-disposition', req.headers['content-disposition'] || 'inline');
res.status(200);
pdfDoc.pipe(res);
pdfDoc.end();
});
However, I cannot do reply.send(pdfDoc).
Update
Since PDFKit's PDFKit.PDFDocument type seems to be a readable stream, I have tried
fastify.get('/streams', function (request, reply) {
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
reply.header('Content-Type', 'application/pdf');
reply.header('content-disposition', 'inline');
reply.send(pdfDoc); // "Failed to load PDF document"
})
I also tried PDFKit's solution of using blob-stream :
fastify.get('/streams', function (request, reply) {
const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
const stream = pdfDoc.pipe(blobStream());
reply.header('Content-Type', 'application/pdf');
reply.header('content-disposition', 'inline');
reply.send(stream); // "Failed to load PDF document"
pdfDoc.end();
})
There are no errors except in the browser.
This is my current solution :
fastify.get('/pdf', async (request, reply) => {
const pdfDoc = printer.createPdfKitDocument(...getDocumentDefinition(request));
const buffer = await new Promise((resolve, reject) => {
const chunks = [];
const stream = new Writable({
write: (chunk, _, next) => {
chunks.push(chunk);
next();
}
});
stream.once('error', (err) => reject(err));
stream.once('close', () => resolve(Buffer.concat(chunks)));
pdfDoc.pipe(stream);
pdfDoc.end();
});
reply.type('application/pdf').code(200).send(buffer);
})
Related
axios script.js file
const creatClient = async (client) => {
try {
const res = await axios({
method: 'POST',
withCredentials: true,
url: '/[url]',
data: client,
}).then(location.assign('/[newUrl]'));
} catch (error) {
console.log(error);
}
};
submitbtn.addEventListener('click', (e) => {
e.preventDefault;
const name = document.getElementById('name').value;
const phone = document.getElementById('phone').value;
const createdATT = new Date(document.getElementById('date').value);
const followUp = new Date(document.getElementById('date2').value);
const images = document.getElementById('img').value;
const insurance = document.getElementById('insurance').value;
const client = { name, phone, insurance, images, createdATT, followUp };
console.log(client);
client ? creatClient(...client) : console.log('no object created');
});
controller file
the console log for req.body [Object: null prototype] {*** the object ***}
const multer = require('multer');
const Client = require('../models/clientModel');
const multerStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/img');
},
filename: (req, file, cb) => {
const ext = file.mimetype.split('/')[1];
cb(null, `user-${Date.now()}.${ext}`);
},
});
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cd(console.log('select image'), false);
}
};
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter,
});
exports.uploadImages = upload.single('images');
//
exports.createClients = async (req, res, next) => {
try {
if (req.file) req.body.images = req.file.filename;
const newClient = { ...req.body };
await Client.create(req.body).then(
res.status(200).json({
status: 'success',
newClient,
})
);
} catch (err) {
console.log(err);
}
};
also with postman sending request give success response with no errors
i've tried location.replace() but also it didn't work for me
and is there another trick from server to get to the desired location out from client side
then accepts a callback as a parameter.
then(() => location.assign('/[newUrl]'))
I have stored the file after uploading it to the downloads folder in my project directory.
I want to download that saved file from the frontend.
When I click on the download button, it doesn't fetch the file.
And when I go to http://localhost:5000/download on the express app, I got this error message
Error: Can't set headers after they are sent.
Express Server Code:
app.get('/download', (req, res) => {
res.send('file downloaded')
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
Frontend App code:
HTML:
<button class="download-btn">download</button>
Script:
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/http://localhost:5000/download");
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);
Folder Structure:
Update:
Server.js
const uploadFiles = async (req, res) => {
const file = await req.files[0];
console.log(file)
postmanCollection = file.path;
outputFile = `downloads/${file.filename}.yml`
convertCollection();
res.json({ message: "Successfully uploaded files" });
}
app.post("/upload_files", upload.array("files"), uploadFiles);
Anyone please help me with this.
You are already using res.send ,which sends the response headers back to client ,which ends the request response cycle ,and when you try to do res.download it throws error. Use instead
app.get('/download', (req, res) => {
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
res.send('file downloaded')--->remove this line
You need to update your js code as well
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/download"); //http://localhost:5000--->this is not required
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);
I am working with the GridFS library in express and node. I'm trying to create multiple buckets. For example I already have a bucket titled avatars, which stores images.
/* Start of mongo connection for uploading files */
const mongoURI = "mongodb://localhost:27017/PTAdata";
const conn = mongoose.createConnection(mongoURI);
let gfs;
conn.once('open', () => {
gfs = stream(conn.db, mongoose.mongo);
gfs.collection('avatars');
})
const storage = new GridFs({
url: "mongodb://localhost:27017/PTAdata",
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
file.user = req.body.username
const name = file.originalname
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: file.user,
bucketName: 'avatars'
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
I now want to create another bucket called audio which will store mp3 files. I checked the documentation for GridFS at https://docs.mongodb.com/manual/core/gridfs/ and it states that "you can choose a different bucket name, as well as create multiple buckets in a single database." However it does not provide any insight or steps to how. Has anyone done any work with the GridFS library and know how to create multiple buckets?
You need to store another "new GridFS" object in a different variable, than pass it to multer as a different storage property. In your case, this should work:
const storage = new GridFs({
url: "mongodb://localhost:27017/PTAdata",
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
file.user = req.body.username
const name = file.originalname
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: file.user,
bucketName: 'avatars'
};
resolve(fileInfo);
});
});
}
});
const anotherStorage = new GridFs({
url: "mongodb://localhost:27017/PTAdata",
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
file.user = req.body.username
const name = file.originalname
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: file.user,
bucketName: 'mp3files'
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
const uploadSongs = multer({ storage: anotherStorage });
Finally, you should choose between those buckets accordingly to your endpoints, for example:
app.post('/api/uploadAvatar', upload.any(), (req, res)=> {
... do stuff
}
app.post('/api/uploadMp3', uploadSongs.any(), (req, res)=> {
... do stuff
}
For me, it made sense to change gfs.collection() in each case (inside the file: (req, file) function), but it worked without changing as well. Be aware that any() is just an option, an it's not the safest one, but it's great for testing your code.
I'm attempting to handle file uploads using a Google Cloud Function. This function uses Busboy to parse the multipart form data and then upload to Google Cloud Storage.
I keep receiving a ERROR: { Error: ENOENT: no such file or directory, open '/tmp/xxx.png' error when triggering the function.
The error seems to occur within the finish callback function when storage.bucket.upload(file) attempts to open the file path /tmp/xxx.png.
Example code
const path = require('path');
const os = require('os');
const fs = require('fs');
const Busboy = require('busboy');
const Storage = require('#google-cloud/storage');
const moment = require('moment');
const _ = require('lodash');
const projectId = 'xxx';
const bucketName = 'xxx';
const storage = new Storage({
projectId: projectId,
});
exports.uploadFile = (req, res) => {
if (req.method === 'POST') {
const busboy = new Busboy({
headers: req.headers
});
const uploads = []
const tmpdir = os.tmpdir();
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const filepath = path.join(tmpdir, filename)
var obj = {
path: filepath,
name: filename
}
uploads.push(obj);
var writeStream = fs.createWriteStream(obj.path);
file.pipe(writeStream);
});
busboy.on('finish', () => {
_.forEach(uploads, function (file) {
storage
.bucket(bucketName)
.upload(file.path, {
name: moment().format('/YYYY/MM/DD/x') + '-' + file.name
})
.then(() => {
console.log(`${file.name} uploaded to ${bucketName}.`);
})
.catch(err => {
console.error('ERROR:', err);
});
fs.unlinkSync(file.path);
})
res.end()
});
busboy.end(req.rawBody);
} else {
res.status(405).end();
}
}
Solved this with a stream instead of a temporary file. Only handles a single file at the moment though.
https://gist.github.com/PatrickHeneise/8f2c72c16c4e68e829e58ade64aba553#file-gcp-function-storage-file-stream-js
function asyncBusboy(req, res) {
return new Promise((resolve, reject) => {
const storage = new Storage()
const bucket = storage.bucket(process.env.BUCKET)
const fields = []
const busboy = Busboy({
headers: req.headers,
limits: {
fileSize: 10 * 1024 * 1024
}
})
busboy.on('field', (key, value) => {
fields[key] = value
})
busboy.on('file', (name, file, fileInfo) => {
const { mimeType } = fileInfo
const destFile = bucket.file(fileName)
const writeStream = destFile.createWriteStream({
metadata: {
contentType: fileInfo.mimeType,
metadata: {
originalFileName: fileInfo.filename
}
}
})
file.pipe(writeStream)
})
busboy.on('close', function () {
return resolve({ fields })
})
if (req.rawBody) {
busboy.end(req.rawBody)
} else {
req.pipe(busboy)
}
})
}
So I am trying to send the data from the client to server and then at the server I am creating a new File and in the router I am sending back response to download that file.
But I could not achieve it. I am using AJAX call. Following is my code:
My Ajax call on clicking a button:
$.ajax({
type: 'POST',
url: '/createDownloadFile',
data: JSON Object,
}).done(() => {
window.open('/download');
});
In express.js:
app.post('/createDownloadFile', (req, res) => {
downloadFile.createDownloadFile(req);
res.send('SUCCESS');
});
downloadFile in a JS below:
const fs = require('fs');
const path = require('path');
module.exports.createDownloadFile = (request) => {
if (request) {
let filePath;
const userID = 'xyz';
filePath = path.join(__dirname, userID.concat('.txt'));
const dataToWrite = request.body;
fs.openSync(filePath, 'w', (err) => {
if (err) throw new Error('FILE_NOT_PRESENT');
});
fs.appendFileSync(filePath, JSON.stringify(dataToWrite, null, 4), (err) => {
if (err) throw new Error('FILE_WRITE_ERROR');
});
return filePath;
}
};
Also, in my router.js file:
router.get('/download', (req, res) => {
const filePath = makeDownloadFile.createDownloadFile(req);
res.download(filePath);
});
But seems like when I invoke the AJAX call it creates the file but unable to write in the file?
What I am missing?