I'm generating HTML webpage as PDF, and then exporting it locally. How can I save this file to my node server and upload to S3
Please find the attached psuedo code
const convertDataToPdf = (exportFlag,cb)=>{ //set to switch between export and save
const doc = new jsPDF();
//... adding metadata and styling the pdf
if(exportFlag) {
doc.save('sample.pdf') //export PDF locally
} else {
cb(doc.output()) //this converts the PDF to raw to send to server
}
}
Based on a this answer, I'm appending the raw PDF data to a new FormData object, and then an ajax call to post the raw data to my nodejs server
convertDataToPdf(false, pdfData => {
let formData = new FormData();
formData.append(`file-1`, pdfData)
$.ajax({
url: '/file-upload',
data: formData,
processData: false,
contentType: false,
type: 'POST',
}).then(data => {
console.log('PDF upload to s3 successful!', data)
}).catch(err => {
console.log('Error! PDF Upload to S3 failed', err)
})
});
});
Now, how can I parse the raw PDF data on the server and upload it?
As an alternative, is it possible to save my file locally and then upload the file to s3?
First question - you can use on Node server multer https://www.npmjs.com/package/multer . This way you don't have to decode pdf. You just handle request and pass file to S3 (via S3 node API). You can use mimetype to be sure someone is sending you pdf.
For sure if you've got application server such as Nginx, you can limit transfer file.
For example in Nginx client_max_body_size 10M;. It's more secure to check limit on server, because naughty users can always cheat your web validations. Multer also has size validation if you would like to return specific exception from your backend.
Related
I need to upload an image to google drive via nodejs after cropping it by a image cropping library. Previously I was uploading images with file input field, so i can get buffer(using express-fileupload library) of the image in backend(nodejs). Now the problem is, after cropping I have image in the form of
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAA....
How to send images in this form to backend such that we could get buffer of that image so as to upload to google drive. Else could we upload this directly to google drive in frontend(javascript)? .I tried using FormData but I can get only string not buffer.
It depends on how your backend and frontend are connected because you will be not able to transfer the image in this form to the backend using HTTP POST
as the maximum size for a HTTP POST is 64KB
However it is very possible to send the image in a blob form to your backend using a WebSocket/WebRTC Library like Socket.io
For example lets take a express backend and a traditional static html homepage with some javascript
example
//my webpage which has the blob this is some browser javascript connected to a html file
//Hosted on http://localhost:3000
var base64data
//I have a cake.png in my static directory
fetch("cake.png")
.then(response => response.blob())
.then(blob => {
const fileReader = new FileReader();
fileReader.readAsDataURL(blob);
fileReader.onloadend = function() {
base64data = fileReader.result;
}
})
.catch(error => {
console.error(error);
});
const socket = io("http://localhost:4000")
socket.on('connect',()=>{
//Sent this blob to the server
socket.emit('blob',base64data)
})
My nodejs file which has a socket.io and drive api
Socket.io Server hosted on http://localhost:4000
const { Server } = require("socket.io");
const google = require("googleapis");
const io = new Server(4000, {
cors: {
"Access-Control-Allow-Origin": "*",
methods: ["GET", "POST", "OPTIONS"],
},
});
/*Initialize the drive
*......
*......
*/
io.on("connection", (socket) => {
socket.on("blob", (blob) => {
drive.files.create({
requestBody: {
name: "image.png",
mimeType: "image/png",
},
media: {
mimeType: "image/png",
body: blob,
},
});
});
});
There fore the blob can be used as a input for body while creating files
For some reason, I don't want to share the URL public (sercet url),
My workflow is as below:
Client send API request to my server:
Api: mywebsite.com/api/image_abc.jpg
I have Nodejs express server to fetch the file from the url:
Eg: sercret_url.com/image_abc.jpg
And then from response image content from Nodejs, I send back the image content to the client and display as image_abc.jpg
I looked around on stackoverflow, but just got an answer from reading file from disk and send to client. What I want is just redirect the image content to client, not saving file to the disk.
Thank you.
Assuming you want to return the contents of a file from a certain URL to the client as buffer here's the solution I suggest
Get the file using axios and return the buffer to you client
const axios = require('axios');
let URL='some-valid-url'
const response = await axios.get(
URL,
{ responseType: 'arraybuffer' }
);
const buffer = Buffer.from(response.data, 'utf-8');
res.status(200).send(buffer);
In case you want to save it to your server you can use fs as follows to write the file to the folder of your choice
fs.writeFile(fileName, buffer, (err) => {
if(!err) console.log('Data written');
});
I've read this article about google drive implementation in nodejs. I want to give to the users of the app the ability to upload the processed files from the app to their google drive account. The article show how to implement a nodejs solution, but since the server will run on localhost, how I can authorize the user on the client side using vuejs?
I've found this question but it's very old and I'm not sure if can really help me at all.
At the moment my nodejs script will save the processed files on the users machine using fs.writeFile.
// espress endpoint
this.app.post('/processFiles', async (req, res) => {
for(let file in req.files){
//console.log(req.files[file]);
await this.composeData(req.files[file]);
}
res.setHeader('Content-Type', 'application/json');
res.send({processStatus: '', outputPath: this.tmpDir});
});
// processing file method
async composetData(file){
//some file compression stuff
this.output = path.format({dir: this.tmpDir, base: file.name});
await fs.writeFile(this.output, this.processedData);
}
Since I want to implement a client side solution, I'm thinking to add an endpoint to my express server that will send processed files back so the client code can do the gdrive upload.
//vue app code
processFiles(){
const formData = new FormData();
this.selectedFiles.forEach( (file, i) => {
formData.append(`file${i}`, file);
});
axios({
method: 'POST',
url: 'http://localhost:9000/processFiles',
data: formData
}).then( (res) => {
//here i want to implement gdrive upload
console.log(res);
});
}
Can anyone provide me some help about?
So I will explain the problem:
Steps:
1) client (browser javascript) sends an Ajax request to the server that hits a controller method called download.
2) the controller's method creates a PDF resource(without saving on the filesystem), and returns a response with the PDF binary stream back to the client.
3) the client receives the PDF binary stream and download it on the client's computer. Is that possible?
Code:
Things I have already tried -
Client-side:
<script>
(function($) {
var button; // some random DOM button
button.on('click', function(e) {
e.preventDefault();
$.ajax({
url: "/download/:userId"
method: "POST",
dataType: "json"
success: function(response) {
var reader = new FileReader;
var file = new Blob([response.pdf_stream], 'application/pdf');
// create a generic download link
var a = $('<a/>', {
href: file,
download: response.filename
});
// trigger click event on that generic link.
a.get(0).click();
}
});
}
})(jQuery);
</script>
On the server-side:
class Controller
{
public function download($userId)
{
// fetching the user from the database
$user = User::find($userId);
// creating a pdf file using barry pdfdom package
// this will actually parse an HTML view and give us the PDF blob.
$pdf = PDF::loadView('pdf.view')->output();
// using Laravel helper function
return response()->json([
'pdf_stream' => utf8_encode($pdf),
'filename' => 'blahblah.pdf"
]);
// Or if you will in native PHP, just in case you don't use laravel.
echo json_encode([
'pdf_stream' => utf8_encode($pdf),
'filename' => 'blahblah.pdf"
]);
}
}
Any Idea what am I doing wrong here? How could I download that PDF file without saving it to the system (security and space concerns).
Any help would be appreciated.
Eden
If you want download pdf on client side, just open this pdf in new window. Use GET request for that things, like in RESTfull application (e.g. download/user/:id or somehow like that).
Could be useful:
Download and open pdf file using Ajax
The main problem is the returned response from controller. Try this:
public function download($userId)
{
// fetching the user from the database
$user = User::find($userId);
// creating a pdf file using barry pdfdom package
// this will actually parse an HTML view and give us the PDF blob.
$pdf = PDF::loadView('pdf.view')->output();
return response($pdf, 200,
[
'Content-Type' => 'application/pdf',
'Content-Length' => strlen($pdf),
'Cache-Control' => 'private, max-age=0, must-revalidate',
'Pragma' => 'public'
]
);
About calling the route which executes download($userid) method:
You do not have to use Ajax. Easy way:
Click view PDF
I have a react frontend and a Rails backend. I am using the paperclip gem and aws-sdk gem in order to upload images to my app. The whole idea is that a user can upload a comment and in that comment there can be an attachment. I think maybe I am sending my backend the wrong data though because it is not working.... On the frontend i'm using file reader to make a file object and sending my rails app the file object -- but it doesn't seem to be recognizing it. If i want my rails backend to recognize this image attachment, how should the image be formatted in my frontend before i send it to my rails app?
This is how my file object is stored in the state -- on handleChange of course:
handleFileChange = (e) => {
debugger
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
file: file,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file)
}
then when i submit the form i use a callback that eventually sends that file object to my adapter which makes the call to the rails backend as sseen below:
static addComment(comment, project_id, datetime, clientView, file) {
debugger
return fetch(path,{
method: 'POST',
headers: headers(),
body: JSON.stringify({
client_view: clientView,
comment_text: comment,
project_id: project_id,
currenttime: datetime,
commentasset: file
})
})
.then( resp => resp.json())
}
so im sending my rails the file object in the param that is commentasset... but when i hit my byebug on my rails end and check
params[:commentasset]
it appears to be blank. Why isnt it sending back my file object?
You're sending a request with JSON including the file as body. Try to send request using FormData instead:
static addComment(comment, project_id, datetime, clientView, file) {
debugger
const body = FormData.new();
body.append('client_view', clientView);
body.append('comment_text', comment);
body.append('project_id', project_id);
body.append('currenttime', datetime);
body.append('commentasset', file);
return fetch(path,{
method: 'POST',
headers: headers(),
body: body
})
.then( resp => resp.json())
}