I have a small part of my app that allow users to upload 5 different images and as soon as an image has been uploaded it show a small thumbnail of it.
Since I upload a single image everything works fine.
As soon as I try to upload multiple image, let's sat 3 all together, it end up to upload 3 equal images showing 3 equal thumbnails... And I'm getting crazy.
Here is the code that handle this part:
HTML:
<input id="pics" type="file" accept="image/*" class="upload" data-imageid="" data-imageurl="" data-callback="displayPics" data-image-type="pics" multiple />
JS:
const fileInputs = document.querySelectorAll('input[type="file"]');
[].slice.call(fileInputs).forEach(fileInput => {
fileInput.addEventListener('change', () => {
uploadFile(
fileInput,
fileInput.getAttribute('data-callback')
);
});
});
unction uploadFile(input, callback) {
let formData = new FormData();
Object.keys(input.files).forEach(current => {
formData.append('file', input.files[current]);
fetchAPI({
target: 'pics/',
method: 'POST',
body: formData
})
.then(data => {
if (callback !== undefined) {
window[callback](data.response);
}
}, err => {
// err...
});
});
}
Can I know what is wrong here? Thanks
fetch() call does not have a URL set. "target" is not a valid options property of object passed to fetch().
You also do not call Response.json() to get a JavaScript object representation of Response.
The code at Question makes a request for each File object instead of a single request.
function uploadFile(input, callback) {
let formData = new FormData();
for (let file of input.files) {
// or set property to `file[N]`
// "file["+ Array.from(formData.keys()).length +"]"
formData.append("file", file)
}
fetchAPI('pics/', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (callback !== undefined) {
window[callback](data.response);
}
}, err => {
// err...
})
}
Related
I would like to know how to share a base64 string as an image over WhatsApp primarily.
I have a react app that generates a QR code and renders it using <img src={base64String} />. Now that img is wrapped around an <a><a/> tag with download attribute, it is working fine. But what I want to accomplish is to be able to have a share button that can share the image on WhatsApp.
I don't want to store it locally or involve file reading if possible because I had a lot of issues implementing it since the QR code will be discarded after download and need not be stored as a file. But if that is the right way I'm ok to work with it.
I tried creating a blob and sharing by using atob(), but that returned an error saying "string not encoded properly" which I didn't want to dig into since it was able to render it as an image and also download by which I assumed the base64 string was correct.
code for Generator:
const GenerateQRCode = (e) => {
e.preventDefault()
QRCode.toDataURL(value, {
margin: 2,
color: {
dark: '#000000',
light: '#ffffff'
},
width: 2000,
height:2000
}, (err, value) => {
if (err) return console.error(err)
console.log(value)
setQr(value)
console.log(data);
})
}
const handleShare =(e)=>{
}
code for that renders the image:
{qr && <>
<img src={qr} alt='' className='qrimg' id='can'/>
<a href={qr} download={`${state.name}.png`} ><button value="Download" >Download</button></a>
</>
}
<button type="" onClick={handleShare}></button>
So, I want that share button to send an image over WhatsApp. My previous attempts for handleShare were messy so I wanted to get fresh inputs or a Solution to do this the right way.
Finally success, that was a hard one! You have to upload the file as a Media into the API and then use that uploaded Media ID to send. Check out the code and replace "xxx" with your values.
Note: Your QR code must be a PNG for this to work. This is not a snippet because it doesn't make sense to run
<img src="data:image/png;base64,iVBORw0KGg...VERY_LONG_STRING_HERE...">
<script>
// Your auth token. Keep it scret, keep it safe.
const Authorization = "Bearer xxx"
const type = 'image/png' // Image type (cannot be gif due to API restriction!)
const baseUrl = "https://graph.facebook.com/v15.0/xxx" // Phone number of sender
const recipient = "xxx" // Phone number of recipient
/** This function creates a File object from an img element which has a base64 image */
function fileFromImg(img) {
var byteString = atob(img.src.split(',')[1]);
var arrayBuffer = new ArrayBuffer(byteString.length);
var arr = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i++) {
arr[i] = byteString.charCodeAt(i);
}
const blob = new Blob([arrayBuffer], { type });
return new File([blob], "qr.png", { type })
}
/** This function will upload a File and call the given callback with the result */
function sendFile(f, callback) {
const url = baseUrl + "/media";
const formData = new FormData()
formData.append('file', f)
formData.append('type', type)
formData.append('messaging_product', "whatsapp")
fetch(url, {
method: 'POST',
body: formData,
headers: { Authorization }
})
.then(r => r.json())
.then(callback)
.catch(console.error)
}
/** This takes in a successful upload and sends that forward to receiver */
function handleResponse(response) {
// When it works response will look like {"id": "xxx"}
fetch(baseUrl + "/messages", {
method: 'POST',
body: JSON.stringify({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": recipient,
"type": "image",
"image": response
}),
headers: { Authorization, "Content-Type": "application/json" }
})
.then(r => r.json())
.then(r => console.log(r))
.catch(console.error)
}
// I choose to trigger the sending when anything is clicked
document.addEventListener("click", () => {
const img = document.querySelector("img")
const file = fileFromImg(img)
sendFile(file, handleResponse)
})
</script>
The recipient will see this;
How can I send an image along with a text in VueJs to my backend ExpressJs?
Right now, what I did was create two http post request
NOTE this.albumName and this.albumDesc are just text and the formData is an image.
createAlbum() {
const formData = new FormData();
for (let file of Array.from(this.myAlbumImages)) {
formData.append("files", file);
}
if (this.albumName) {
axios
.post("http://localhost:9001/image/album", {
ALBUM: this.albumName,
DESCRIPTION: this.albumDesc
})
.then(resp => console.log(resp))
.catch(err => console.log(err));
setTimeout(function() {
axios
.post("http://localhost:9001/image/album", formData)
.then(resp => console.log(resp))
.catch(err => console.log(err));
}, 3000);
this.albumName = "";
this.albumDesc = "";
} else {
alert("Please fill the above form.");
}
},
and here is my Backend.
This creates the folder based on the passed data and it also creates a named undefined folder
router.post('/album', (req, res) => {
let sql = "INSERT INTO GALLERY SET ALBUM = ?, DESCRIPTION = ?";
let body = [req.body.ALBUM, req.body.DESCRIPTION]
myDB.query(sql, body, (error, results) => {
if (error) {
console.log(error);
} else {
let directory = `C:/Users/user/Desktop/project/adminbackend/public/${req.body.ALBUM}`;
fse.mkdirp(directory, err => {
if (err) {
console.log(err);
} else {
console.log(directory);
}
})
}
})
I think this is because of NodeJS is Asynchronous that's why it creates the undefined folder.
Reason for behavior you see is you are sending two different requests to the same route. 1st includes ALBUM and DESCRIPTION form field values, but not the files. Second (inside setTimeout) will contain just files and no other fields, so referencing them like req.body.ALBUM will return undefined
You can send all data (text fields and files) in one request. Just do this:
const formData = new FormData();
for (let file of Array.from(this.myAlbumImages)) {
formData.append("files", file);
}
formData.append("ALBUM", this.albumName);
formData.append("DESCRIPTION", this.albumDesc);
axios.post("http://localhost:9001/image/album", formData)
.then(resp => console.log(resp))
.catch(err => console.log(err));
FormData always uses content type multipart/form-data. To parse it on server side you need Express middleware that parses multipart forms, and gives you access to both fields and image/s. For example multer...
for the first part the client may can help you this link How to post image with fetch?
const fileInput = document.querySelector('#your-file-input') ;
const formData = new FormData();
formData.append('file', fileInput.files[0]);
const options = {
method: 'POST',
body: formData,
// If you add this, upload won't work
// headers: {
// 'Content-Type': 'multipart/form-data',
// }
};
fetch('your-upload-url', options);
and for the seerver part cant help you this link
Node Express sending image files as API response
app.get('/report/:chart_id/:user_id', function (req, res) {
res.sendFile(filepath);
});
and oficial documentation about this
http://expressjs.com/en/api.html#res.sendFile
I pass image data by appending with formdata in reactjs
This is the handler for this
handleUserImage(e) {
const input = e.target
if (input.files && input.files[0]) {
const reader = new FileReader()
reader.onload = (r) => {
const formdata = new FormData()
formdata.append('photos', input.files[0])
formdata.append('fileName', input.files[0].name)
this.props.uploadImage({ image: formdata })
}
reader.readAsDataURL(input.files[0])
}
}
and when I console it to backend it looks like blank object
{ image: {} }
Then how can I pass formdata and upload the image
Edit : Upload image api call
export const uploadImage = (data) => {
console.log(data)
return fetch(`http://hostName:3001/town/image`, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data'
},
body: data
})
.then((res) => {
return res.json()
})
.then((payload) => {
console.log(payload)
return payload
}).catch((error) => {
throw error
})
}
when I console it to backend it looks like blank object
This is normal.
headers: {
'Content-Type': 'multipart/form-data'
},
Multipart form data MIME types need to have a parameter to describe the boundary marker (so parsers know where one field ends and the next begins).
Yours is missing it. What's more, there is no way for you to know what it will be.
Don't override the Content-Type header, allow fetch to generate it from the FormData object. Remove the code I quoted entirely.
Why use FileReader, upload it as base64 and append file name separately?
you can just do
handleUserImage (evt) {
const file = evt.target.files[0]
if (file) {
const formdata = new FormData()
formdata.append('photos', file)
this.props.uploadImage({ image: formdata })
}
}
You save a few bytes,
avoid unnecessary decode & encode calculations
runs faster
saves ~3x bandwidth
FormData.append(name, value, filename)
name
The name of the field whose data is contained in value.
value
The field's value. This can be a USVString or Blob (including subclasses such as File).
filename Optional
The filename reported to the server (a USVString), when a Blob or File is passed as the second parameter. The default filename for Blob objects is "blob". The default filename for File objects is the file's filename.
-- MDN - FormData.append()
I use fetch to upload images in react,my code is like below:
let formData = new FormData();
let file = {uri: imgdata, type: 'multipart/form-data', name: '2_resources.jpg'};
formData.append("name", "name");
formData.append("mobile", "18381307123");
formData.append("content", "123654");
formData.append("resources", file,"2_resources.jpg");//mind this line
fetch(Config.report,
{
mode: 'cors',
method: "POST",
body: formData
})
.then(res => res.json())
.then((data) => {
console.log(data)
}
).catch((err) => {
console.log(err)
}
);
but,run it I get the request :
I have look for FormData API document on https://developer.mozilla.org/en-US/docs/Web/API/FormData
it have write below:
and the fetch used is :"isomorphic-fetch": "^2.2.1"
what should I do to use fetch upload images? thanks.
You are adding the file to the FormData object incorrectly. isomorphic-fetch wraps github's fetch polyfill. Looking at their docs shows that it should be:
handleFileUpload(event) {
var formData = new FormData();
formData.append('fileName', event.target.files[0]);
fetch('http://your-domain/upload',
{
mode: 'cors',
method: 'POST',
body: formData
}).then(function (response) {
console.log('response: ', response);
});
}
The field name on the server will be whatever you use as the key when appending formData. In the above example it will be 'fileName'. I also don't think you need to set the content-type and file name yourself. It should get set automatically.
In the above example, 'event' is the event fired from the input tag when the user upload the file:
<input type="file" onChange={this.handleFileUpload}/>
I'm trying to upload files from browser to s3 amazon, I've modified the CORS policy rules to allow the post for the bucket, but I'm getting the error
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>InvalidArgument</Code><Message>Bucket POST must contain a field named 'key'. If it is specified, please check the order of the fields.</Message>
<ArgumentValue></ArgumentValue><ArgumentName>key</ArgumentName><RequestId>1E0A8DC78C0CEA9A</RequestId><HostId>XN38Qje9hUrGqHNIhtT8CtowX9tXlpyfEoaXb1UNxlsyLOWreh2mKqKVXg1zjLVl</HostId></Error>
Here is my request and response, I'm passing key parameter in the right order by still getting this error
Can anyone tell me whats wrong with it, I'm submitting request using FormData
any help would be greatly appreciated.
Thanks
Edit: here is the code pls check
var form_data = new FormData();
form_data.append('file',hdlr.file);
//form_data.append('crop_type',settings.get_cropped_type());
//form_data.append('attributes',JSON.stringify(file_attr));
$('input:hidden',$form).each(function(){
form_data.append(this.name,this.value);
});
//finally post the file through AJAX
var xhr = new XMLHttpRequest();
xhr.open("POST", $form[0].action, true);
xhr.send(form_data);
It kind of looks like your file form field is appearing first in the request. I can't tell for sure since you have not included the entire request payload in your answer, but it looks like this is appearing just above the "key" field. AWS ignores all fields in the request after the file field, so all other fields must appear before the file.
Thank you Ray Nicholus
It works for me.
{
"formAttributes": {
"action": "https://**.s3.ap-southeast-1.amazonaws.com",
"method": "POST",
"enctype": "multipart/form-data"
},
"formInputs": {
"acl": "public-read",
"key": "users/2/images/drops-of-water-578897_640.jpg",
"X-Amz-Credential": "**",
"X-Amz-Algorithm": "AWS4-HMAC-SHA256",
"X-Amz-Date": "**",
"Policy": "**",
"X-Amz-Signature": "**"
}
}
function uploadFile(event) {
event.preventDefault();
getSignedPost().then(() => {
const fileEl = document.getElementById('id-file');
const file = fileEl.files[0];
const formData = new FormData();
Object.keys(uploadCredentials.formInputs).forEach((key) => {
formData.append(key, uploadCredentials.formInputs[key]);
});
// update key to file name
const key = `users/2/images/${file.name}`;
formData.set('key', key);
uploadCredentials.formInputs.key = key;
// update show data on page
const el = document.getElementById('id-upload-info');
el.innerText = JSON.stringify(uploadCredentials, null, 2);
// IMPORTANCE: https://stackoverflow.com/a/15235866
// AWS ignores all fields in the request after the file field, so all other fields must appear before the file.
formData.append('file', file);
fetch(uploadCredentials.formAttributes.action, {
method: uploadCredentials.formAttributes.method,
// headers: {
// 'Content-Type': 'multipart/form-data',
// },
body: formData,
})
.then((res) => {
if (res.status === 204) {
console.log('Successfully uploaded file');
console.log('-- 204 - no content');
return `Successfully uploaded file: ${key}`;
}
if (res.ok) {
return res.json();
} else {
return res.text();
}
})
.then((res) => {
alert(JSON.stringify(res, null, 2));
})
.catch((err) => {
alert(err.message || err);
});
});
}