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())
}
Related
Someone could help me, I'm trying to send a photo and caption into a group, but isn't working!
I'd like send the photo that I recieve as base64 and send to a facebook group api.
What I'm doing? I got the base64 then convert into buffer and write it on a local disk, then I read it into a formdata.
I load the data into a form this way =>
const form = new FormData();
const fileContent = Buffer.from(url as any, 'base64');
fs.writeFile('../tmp', fileContent, (err) => {
if (err) return console.log(err)
})
form.append('groupId', groupId)
form.append('caption', caption)
form.append('image ', fs.createReadStream('../tmp'))
In below is the axios configurations and request
await client.post(`${config.facebook.BASE_URL}/${groupId}/photos`, form, {
headers: {
...form.getHeaders(),
Authorization: `OAuth ${config.facebook.token}`,
'content-type': 'multipart/form-data',
file_type: "image/jpeg",
}
})
Note: This way I got the Error: Request failed with status code 500
I already resolved this although I changed de way that the file came to me, instead of receiving the image in base64 I'm receiving a signed url from google storage, and on the form.append, it's necessary to pass the name and the extension of the file, like this => form.append('source', image, 'file.jpg');
of course together with the other params and axios' configurations
I am trying to get this working now for days -.-
Using a simple NodeJS express server, I want to upload an image to a Django instance through Post request, but I just can't figure out, how to prepare the request and embed the file.
Later I would like to post the image, created from a canvas on the client side,
but for testing I was trying to just upload an existing image from the nodeJS server.
app.post('/images', function(req, res) {
const filename = "Download.png"; // existing local file on server
// using formData to create a multipart/form-data content-type
let formData = new FormData();
let buffer = fs.readFileSync(filename);
formData.append("data", buffer); // appending the file a buffer, alternatively could read as utf-8 string and append as text
formData.append('name', 'var name here'); // someone told me, I need to specify a name
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
axios.post("http://django:8000/images/", formData, config)
.then(response => {
console.log("success!"); // never happens :(
})
.catch(error => {
console.log(error.response.data); // no file was submitted
});
});
What am I doing wrong or did I just miss something?
EDIT
I just found a nice snippet with a slighlty other approach on the npm form-data page, on the very bottom (npmjs.com/package/form-data):
const filename = "Download.png"; // existing local file on server
let formData = new FormData();
let stream = fs.createReadStream(filename);
formData.append('data', stream)
let formHeaders = formData.getHeaders()
axios.post('http://django:8000/images/', formData, {
headers: {
...formHeaders,
},
})
.then(response => {
console.log("success!"); // never happens :(
})
.catch(error => {
console.log(error.response.data); // no file was submitted
});
sadly, this doesn't change anything :( I still receive only Bad Request: No file was submitted
I don't really have much Django code just a basic setup using the rest_framework with an image model:
class Image(models.Model):
data = models.ImageField(upload_to='images/')
def __str__(self):
return "Image Resource"
which are also registered in the admin.py,
a serializer:
from .models import Image
class ImageSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Image
fields = ('id', 'data')
using automatic URL routing.
I wrote a simple test script and put the same image on the django server, to verify that image uploads works, and it does:
import requests
url = "http://127.0.0.1:8000/images/"
file = {'data': open('Download.png', 'rb')}
response = requests.post(url, files=file)
print(response.status_code) # 201
I had a similar problem: I used the same Django endpoint to upload a file using axios 1) from the client side and 2) from the server side. From the client side it worked without any problem, but from the server side, the request body was always empty.
My solution was to use the following code:
const fileBuffer = await readFile(file.filepath)
const formData = new FormData()
formData.append('file', fileBuffer, file.originalFilename)
const response = await fetch(
urlJoin(BACKEND_URL),
{
method: 'POST',
body: formData,
headers: {
...formData.getHeaders(),
},
}
)
A few relevant references that I found useful:
This blog post, even though it seems the author manages to send form data from the server side using axios, I did not manage to reproduce it on my case.
This issue report in the axio repository, where one comment suggests to use fetch.
In your node.js express server instead of adding the image to the form data, try directly sending the stream in the API post.
const filename = "Download.png"; // existing local file on server
//let formData = new FormData();
let stream = fs.createReadStream(filename);
//formData.append('data', stream)
let formHeaders = formData.getHeaders()
axios.post('http://django:8000/images/', stream, {
headers: {
...formHeaders,
},
})
.then(response => {
console.log("success!"); // never happens :(
})
.catch(error => {
console.log(error.response.data); // no file was submitted
});
I still didn't manage to get this working with axios so I tried another package for sending files as post requests, namely unirest, which worked out of the box for me.
Also it is smaller, requires less code and does everything I needed:
const filename = "Download.png"; // existing local file on server
unirest
.post(url)
.attach('data', filename) // reads directly from local file
//.attach('data', fs.createReadStream(filename)) // creates a read stream
//.attach('data', fs.readFileSync(filename)) // 400 - The submitted data was not a file. Check the encoding type on the form. -> maybe check encoding?
.then(function (response) {
console.log(response.body) // 201
})
.catch((error) => console.log(error.response.data));
If I have some spare time in the future I may look into what was wrong with my axios implementation or someone does know a solution pls let me know :D
Here is what I am trying to build:
I have a form with various fields, one of them is image upload. I took the image upload and base64 encode part from here and it works perfectly. This all happens on client-side:
document.getElementById('button').addEventListener('click', function() {
var files = document.getElementById('file').files;
if (files.length > 0) {
getBase64(files[0]);
}
});
function getBase64(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
};
reader.onerror = function (error) {
};
}
Then, I would like to take the base64 encoded string and include it in JSON to make an api call. The api call needs to happen on server-side. It also includes authentication, and this bit alone is also working fine. The call creates a file in the selected location.
The bit that I am struggling with:
How do I pass the fileEncoded variable from the client-side part of the script to the server-side?
I tried the following:
Passing it as query string to a separate page for making the api call: <form action="apicall.html?fileEncoded" method="post"> but I couldn't make this work
Using localStorage, but that didn't work either: localStorage.setItem("fileEncoded", fileEncoded);
I would like to understand what I am missing here and I would appreciate an answer that would explain the concept, on top of providing a code snippet.
I would prefer to do it on two separate pages (1st with form and base64 encode, 2nd with the server-side api call).
You likely need to send a request to your server via POST request, with the encoded file data as the body in that request.
Then server-side, you will have to handle that POST request.
For example:
function getBase64(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
console.log(reader.result);
var fileEncoded = reader.result;
fetch("/upload/file", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data: fileEncoded,
// Additional fields here...
})
})
.then(function(res) {
console.log("Success!", res);
})
.catch(function(err) {
console.log("Error!", err);
});
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
JS Bin
In your code you are using readAsDataURL which converts the file into a data URL which is typically used to display a selected image file. It doesn't access the server at all. What you want to do is create an AJAX request to the server.
See related question: How to use FormData for ajax file upload
I have a post fetch request coming from my React client to my remote Flask server like so:
fetch(FETCH_URL, {
method: 'POST',
body: data,
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
var a = response.body.getReader();
a.read().then(({ done, value }) => {
console.log(new TextDecoder("utf-8").decode(value));
}
);
});
response.body comes in the form of a ReadableStream object so I was able to extract a Uint8array which I then decoded to be the contents of the txt file I sent back from my flask server as shown in the code above.
At this point I'm lost, what I'm trying to do is send a request to my remote server with a filename (in the requests' data), and download that file on my computer.
As shown above, I tried a fetch request to my remote server, then in flask, my server finds and opens the file which is stored on the server itself, and sends back the file.
filename = request.get_json()['filename']
f = open(filename)
return f
The problem now is that from what I've read, I can't create a file on my computer just with react. Even so, I don't know if this would work with all types of files or just txt files. Does anyone have any guidance to get to my end goal of downloading a file from a remote flask server.
If your requirement is to create a file with data you received from the response. The below solution should work.
Create the blob object with the text you received
Create Blob Object URL for that blob
Trigger downloading the object using that URL
Since this is pure Javascript solution, it's independent of React or any library you use.
Solution:
fetch(FETCH_URL, {
method: 'POST',
body: data,
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
var a = response.body.getReader();
a.read().then(({ done, value }) => {
// console.log(new TextDecoder("utf-8").decode(value));
saveAsFile(new TextDecoder("utf-8").decode(value), 'filename');
}
);
});
function saveAsFile(text, filename) {
// Step 1: Create the blob object with the text you received
const type = 'application/text'; // modify or get it from response
const blob = new BlobBuilder([text], {type});
// Step 2: Create Blob Object URL for that blob
const url = URL.createObjectURL(blob);
// Step 3: Trigger downloading the object using that URL
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click(); // triggering it manually
}
Alternatively, you can use <Button href="${YOUR_FILE_URL}"/> to download the file sent by flask.
To add to Kamalakannan's post, if you are never going to use that element again make sure to removeChild() and revokeObjectURL() after triggering the click().
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.