I'm working on a React project with Facebook user integration.
I need to post a client-side generated image to a private group on behalf of the logged-in user.
Using the {group-id}/photo endpoint I can successfully post a picture which already exists on the web providing the url and caption parameters:
const postImageUrlToFacebook = () => {
const url = "https://upload.wikimedia.org/wikipedia/commons/e/e0/New.gif";
let caption = "test upload url image from React";
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", { url, caption })
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
};
The definition of the httpFacebookGraphClient is the following:
import axios, { AxiosRequestConfig } from "axios";
const httpFacebookGraphClient = (token: string | null) => {
const defaultOptions = {
baseURL: "https://graph.facebook.com/v14.0",
method: "get",
// withCredentials: true,
headers: {
"Content-Type": "application/json",
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the access token parameter for any request
instance.interceptors.request.use((config: AxiosRequestConfig): AxiosRequestConfig => {
if (!config) {
config = {};
}
if (!config.params) {
config.params = {};
}
config.params.access_token = token;
config.params.limit = "999";
return config;
});
return instance;
};
export default httpFacebookGraphClient;
I would now need to start from a default svg, modifiy some g tags inside of it via javascript, convert to jpg and post it to a facebook group.
Let's suppose the starting SVG is the following:
<svg id="Livello_1" data-name="Livello 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 107"><defs><style>.cls-1,.cls-3,.cls-4,.cls-5{fill:#ffc000;}.cls-2{fill:#fff;}.cls-3,.cls-4,.cls-5{stroke:#fff;stroke-miterlimit:10;}.cls-3{stroke-width:0.87px;}.cls-4{stroke-width:0.79px;}.cls-5{stroke-width:0.65px;}</style></defs><title>bannerino scegli il tuo sconto</title><path class="cls-1" d="M136.88,2.63a10,10,0,0,1,10,10V94.37a10,10,0,0,1-10,10H13.13a10,10,0,0,1-10-10V12.63a10,10,0,0,1,10-10H136.88m0-2H13.13a12,12,0,0,0-12,12V94.37a12,12,0,0,0,12,12H136.88a12,12,0,0,0,12-12V12.63a12,12,0,0,0-12-12h0Z"/></svg>
I started from the end, trying to post a local image to the Facebook group before trying to build the image, but I'm already stuck.
Reading the Facebook api docs at this link I found this sentence on the url parameter;
The URL of a photo that is already uploaded to the Internet. You must specify this or a file attachment
mentioning a file attachment.
Again on the Facebook docs I found this that explain how to upload a file to Facebook to use it in subsequent api calls, but I can't make it to work.
Anyone could give me a hint on how to proceed?
I found the way around this and I can successfully post an image selected by input field client side.
React component state:
const [fileToUpload, setFileToUpload] = useState<any>();
inputFileChangeHandler:
const inputFileChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.currentTarget != null && event.currentTarget.files != null) {
setFileToUpload(event.currentTarget.files[0]);
}
};
uploadImageToFacebook:
const uploadImageToFacebook = () => {
let caption = "test upload local image from React";
const fileReader = new FileReader();
fileReader.onloadend = async () => {
if (fileReader.result != null) {
const photoData = new Blob([fileReader.result], { type: "image/png" });
const formData = new FormData();
formData.append("source", photoData);
formData.append("caption", caption);
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
};
fileReader.readAsArrayBuffer(fileToUpload);
};
On jsx:
<input type="file" id="file-input" onChange={inputFileChangeHandler} />
<button onClick={uploadImageToFacebook}>Invia</button>
Related
I'm currently developing my first real project for a client with NextJS and MongoDB and I'm having problems uploading images. I'm working with Cloudinary but it can't receive multiple files and I'm also having issues with state management because when the form is submitted my database doesn't receive the files whereas Cloudinary does.
The API works fine so I post here the code of the form (REACT).
export default function NewProduct() {
const initialState = {
title: "",
price: 0,
description: "",
content: "",
images: [],
category: "tortas",
};
const [product, setProduct] = useState(initialState);
const { title, price, description, content, category } = product;
const [files, setFile] = useState("");
//const handleChangeInput = (e) => {
// setProduct({ ...product, [e.target.name]: e.target.value });
//};
const handleUploadInput = async (e) => {
const uploadFiles = [...e.target.files];
setFile([...files, uploadFiles]);
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
for (let file of files) {
formData.append("file", file);
}
formData.append("upload_preset", "balbla");
const res = await fetch(
"https://api.cloudinary.com/v1_1/blabla/image/upload",
{
method: "POST",
body: formData,
}
);
const data = await res.json();
setProduct((p) => ({ ...p, images: data.secure_url}));
await createProduct();
setProduct(initialState);
};
const createProduct = async () => {
try {
const res = await fetch("http://localhost:3000/api/products", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(product),
});
const data = await res.json();
console.log(data);
} catch (err) {
console.log(err);
}
};
return (
<Layout>
<div className={styles.formDiv}>
<form className={styles.form} onSubmit={handleSubmit}>
<input
type="file"
name="file"
onChange={handleUploadInput}
multiple
accept="image/*"
/>
<button type="submit">Crear</button>
</form>
</div>
</Layout>
);
}
In case using Cloudinary isn't the best option with NextJS, what other cloud or stuff I could use?
I hope I made myself clear.
Thank you in advance.
The Cloudinary Upload API unfortunately doesn't support uploading multiple resources within a single request, so you would need to likely loop through all of your media items and upload them each individually.
As far as the production creation is concerned, I can't tell for sure, but you may be trying to create the product before your product state is updated.
Inside createProduct have you checked to see if all of the data you expect is available at the time it's being ran?
You could try listening to updates to the product state and create a product based off of that with a useEffect hook, for instnace:
useEffect(() => {
// Check if product is available or perform
// a check that you know isn't ready to create yet
if ( !product ) return;
(async function run() {
await createProduct(product);
setProduct(initialState);
})()
}, [product])
Multer worked fine when I upload multiple images by using Postman but when I try to send files as an array from the front-end, it always return files is undefined and files is not iterable.
I think the problem is in the front-end React. How to fix this problem?
Back-end
router.post('/multer',auth ,upload.array('image'), async (req, res) => { //NOTE image is the field name
try {
const urls = []
const files = req.files;
console.log('Upload cloudinary running '+ files)
.
.
.
Front-end React
import React, {Fragment, useState, useEffect} from 'react';
const Dashboard = ({auth: { user, loading }}) => {
.
.
.
const [files, setFiles] = useState([]);
const handleChange = e => {
const file_reader = new FileReader();
const file = e.target.files[0];
let file = e.target.files[0];
file_reader.onload = () => {
setFiles([...files, { image: file_reader.result }]);
};
file_reader.readAsDataURL(file);
}
const handleSubbmitFile = (e) => {
e.preventDefault();
if(!files) return;
uploadImage(files);
}
const uploadImage = async (base64EncodedImage) => {
try {
const config = {
headers:{
'Content-Type' : 'application/json'
}
}
const body = JSON.stringify({files: base64EncodedImage});
await axios.post('/api/blogs/multer', body, config);
} catch (error) {
console.log(error);
}
}
return(
<form onSubmit={handleSubbmitFile} className="form-outline">
<input name="image" onChange={handleChange} type="file"
class="form-control-file" id="exampleFormControlFile1"
accept="image/*"/>
</form>
)
}
I found the problems and I fixed it by using formData in handleSubbmitFile and set the property filed to image by using append.
const handleSubbmitFile = (e) => {
e.preventDefault(); //NOTE prevent from reload the page
let formData = new FormData();
for(var i = 0 ; i < files.length; i++){
formData.append('image',files[i])
}
if(!files) return;
uploadImage(formData);
}
Another problems is that using Json.stringify() before using axios.
I didn't use Json.stringify() to convert formData before sending it to back-end via axios. As a result, Multer work well without problems
const uploadImage = async (formData) => {
try {
const config = {
headers:{
'Content-Type' : 'application/json'
}
}
await axios.post('/api/blogs/multer', formData, config);
} catch (error) {
console.log(error);
}
}
Basically I want to record audio and I'm using react-mic for this but it gives back blob but I want mp3 or wav file to send to the backend. How to POST blob file to the server?
This is my code in App.js
import React from "react";
import { ReactMic } from "react-mic";
import $ from "jquery";
import { findDOMNode } from "react-dom";
import "./App.css";
class App extends React.Component {
handleDisplayForStart = () => {
const startBtn = findDOMNode(this.refs.startBtn);
$(startBtn).addClass("d-none");
const stopBtn = findDOMNode(this.refs.stopBtn);
$(stopBtn).removeClass("d-none");
};
handleDisplayForStop = () => {
const stopBtn = findDOMNode(this.refs.stopBtn);
$(stopBtn).addClass("d-none");
const processBtn = findDOMNode(this.refs.processBtn);
$(processBtn).removeClass("d-none");
};
constructor(props) {
super(props);
this.state = {
blobURL: null,
recordedBlob: null,
record: false,
};
}
startRecording = () => {
this.setState({ record: true });
this.handleDisplayForStart();
};
stopRecording = () => {
this.setState({ record: false });
this.handleDisplayForStop();
};
onData(recordedBlob) {
console.log("chunk of real-time data is: ", recordedBlob);
}
onStop = (recordedBlob) => {
console.log(recordedBlob.blobURL);
const blobURL = recordedBlob.blobURL;
this.setState({ blobURL: blobURL });
this.onUpload();
return recordedBlob.blobURL;
};
onUpload = (recordedBlob) => {
// var form = new FormData();
// form.append("file", recordedBlob);
// fetch("http://localhost:3000/audio", {
// // content-type header should not be specified!
// method: "POST",
// body: form,
// })
// .then(function (response) {
// return response.text();
// })
// .then(function (text) {
// console.log(text); // The text the endpoint returns
// })
// .catch((error) => console.log(error));
};
render() {
return (
<div className="App">
<ReactMic
visualSetting="frequencyBars"
// mimeType='audio/mp3'
record={this.state.record}
className="d-none"
onStop={this.onStop}
onData={this.onData}
/>
<button
ref="startBtn"
className="start-btn"
onClick={this.startRecording}
type="button"
>
START
</button>
<button
ref="stopBtn"
className="stop-btn concentric-circles d-none"
onClick={this.stopRecording}
type="button"
>
STOP
</button>
<button
ref="processBtn"
className="process-btn d-none"
onClick={this.onUpload}
>
Processing..
</button>
<br />
<audio src={this.state.blobURL} controls />
</div>
);
}
}
export default App;
I tried various things from medium blogs and different StackOverflow answers but anything doesn't work correctly.
This may be a bit late, i also came across this problem and i also couldn't find a solution, sharing the solution so it may help others.
code for sending recorded blob file using react-mic to server,
const onStop = (blob) => {
const formData = new FormData();
// if you print blob in console you may get details of this obj
let blobWithProp = new Blob([blob["blob"]], blob["options"]);
formData.append("file", blobWithProp);
const postRequest = {
method: "POST",
body: formData,
};
fetch("http://127.0.0.1:5000/audio_out/", postRequest)
.then(async (res) => {
const data = await res.json();
console.log(data);
if (!res.ok) {
const err = (data && data.message) || res.status;
return Promise.reject(err);
}
})
.catch((err) => {
console.log(err);
});
};
Here onStop() is the callback function which is executed when recording stopped.
On your commented part of your code you are sending recorded blob file directly. I don't know much about it, but if you log the received file on console you may see it is an object file containing blob, options, etc. So, I think we need to create a new Blob file from that. I am just a beginner in web development, may be i am wrong. But the above code solved the problem.
I am trying to send a picture to the back end, the web api works when I test it with postman. The image file is empty when I send it from the front end.
this code bellow sending from form data
const submit = useCallback(
async (values: PostExtensions) => {
debugger;
setLoading(true);
const date = dayjs(values.date );
values.date = dayjs(date , "YYYY-MM-DDTHH:mm:ss Z").format();
var imgdata = values.file![0];
const formdata: FormData = new FormData();
formdata.append("postId", values.postId!);
formdata.append("file", imgdata);
formdata.append("userId", values.userId!);
formdata.append("projectId", values.projectId!);
formdata.append("date", values.date!);
for (var pair of Array.from(formdata.entries())) {
console.log(pair[0] + ', ' + JSON.stringify(pair[1]));
}
await entitySubmit({ // entity submit is axios extensions for my crud op and state op.
values: formdata,
dispatch: dispatch,
returndata: true,
links: [
{
type: 0,
link: "gider/addpost",
actionType: ActionType.add_Post,
method: "POST",
},
{
type: 1,
link: "gider/editpost",
actionType: ActionType.edit_Post,
method: "POST",
},
],
id: "postId",
})
.then((res) => {
if (res) {
console.log(res);
setLoading(false);
setShow({ ...show, modal: false });
} else {
setLoading(false);
}
})
.catch(() => setLoading(false));
return null;
},
[show, dispatch]
);
The file comes into values.file, but it turns into this shape when sending it.
file field always get this --> file, "[object Object]"
I could'nt post file object to server
EDIT 1: I found the solution, when I try this code
var imgdata = new Blob([values.file![0]], {type : 'image/jpeg'}),
it seems like work, but What if the image is not jpg extension?
I am trying to upload images to an S3 bucket with react and expressjs. When I attempt to upload the image I get a 501 Not Implemented Error. I am using axios to contact the end point I created in the server code.
My react code:
class FileUpload extends Component {
state = {
file: null
};
submitFile = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('file', this.state.file[0]);
axios.post(`test-upload`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
// handle your response;
}).catch(error => {
// handle your error
});
}
handleFileUpload = (event) => {
this.setState({file: event.target.files});
}
render () {
return (
<form onSubmit={this.submitFile}>
<input label='upload file' type='file' onChange=
{this.handleFileUpload} />
<button type='submit'>Send</button>
</form>
);
}
}
export default FileUpload;
My server code:
const uploadFile = (buffer, name, type) => {
const params = {
ACL: 'public-read',
Body: buffer,
Bucket: process.env.S3_BUCKET,
ContentType: type.mime,
Key: `${name}.${type.ext}`
};
return s3.upload(params).promise();
};
app.use('/', (req,res) =>{
res.send(JSON.stringify({ greeting: `Hello!` }));
});
// Define POST route
app.post('/test-upload', (request, response) => {
const form = new multiparty.Form();
form.parse(request, async (error, fields, files) => {
if (error) throw new Error(error);
try {
const path = files.file[0].path;
const buffer = fs.readFileSync(path);
const type = fileType(buffer);
const timestamp = Date.now().toString();
const fileName = `bucketFolder/${timestamp}-lg`;
const data = await uploadFile(buffer, fileName, type);
return response.status(200).send(data);
} catch (error) {
return response.status(400).send(error);
}
});
});
This was done by following a guide on the internet but there seems to be something missing that I just can't figure out.
Figured it out in the end,
axios.post(/test-upload,
should have been
axios.post(http://localhost:3000/test-upload,