I am using react-dropzone and cloudinary for image upload.
I have already made an successfull connection between my account and my react project and I am able to upload images though my react project.
I am having trouble setting the file name in the react project.
Here is the code snipped of my react project.
I have already tried something like this:
onImageDrop(files) {
console.log(files)
var test = JSON.parse(JSON.stringify(files))
test[0]["name"] = "test"
this.handleImageUpload(test);
}
But I get an error saying that the file is readonly.
Here is the working example of what I have
onImageDrop(files) {
this.handleImageUpload(files[0]);
}
handleImageUpload(file) {
let upload = request.post(CLOUDINARY_UPLOAD_URL)
.field('upload_preset', CLOUDINARY_UPLOAD_PRESET)
.field('file', file);
upload.end((err, response) => {
if (err) {
console.error(err);
}
console.log(response)
});
}
render() {
return (
<div>
<Dropzone
onDrop={this.onImageDrop.bind(this)}cloudinary
accept="image/*"
multiple={false}>
{({ getRootProps, getInputProps }) => {
console.log("input props", getInputProps)
return (
<div
{...getRootProps()}
>
<input {...getInputProps()} />
{
<p>Try dropping some files here, or click to select files to upload.</p>
}
</div>
)
}}
</Dropzone>
</div>
)
}
How do I change the file name before I send it to cloudfire?
You can set the image name on Cloudinary when uploading by setting the public_id.
For example:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class Upload extends React.Component {
processFile = async e => {
var file = e.target.files[0];
var formdata = new FormData();
formdata.append("file", file);
formdata.append("cloud_name", "XXXX");
formdata.append("upload_preset", "XXXX");
formdata.append("public_id", "my-name1");
let res = await fetch(
"https://api.cloudinary.com/v1_1/<Cloud-Name>/auto/upload",
{
method: "post",
mode: "cors",
body: formdata
}
);
let json = await res.json();
console.log(JSON.stringify(json.secure_url));
};
render() {
return (
<div>
<h3>Upload</h3>
<input type="file" onChange={this.processFile} />
</div>
);
}
}
ReactDOM.render(<Upload />, document.getElementById("container"));
Related
I have a task for uploading images using Next js. API for image uploading has been given and it exists in the same project and when it becomes successful images are stored in a separate directory.
API for image uploading is as follows,
import formidable from "formidable";
import path from "path";
import { renameSync } from "fs";
export const config = {
api: {
bodyParser: false,
},
};
export default async function upload(req, res) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const form = new formidable.IncomingForm({
keepExtensions: true,
maxFileSize: 200 * 1024 * 1024,
});
return new Promise((resolve, reject) => {
form.parse(req, async (err, fields, files) => {
if (err) {
res.status(500).json({ error: err.message });
return resolve();
}
if (!files.image) {
res.status(422).json({ error: "Bad request: missing image field" });
return resolve();
}
const ext = path.extname(files.image.path);
const uuid = Math.random().toString(26).slice(2);
await renameSync(files.image.path, `./public/images/${uuid}${ext}`);
res.status(201).json({
uuid,
url: `/images/${uuid}${ext}`,
});
return resolve();
});
});
}
Code for the front-end is mentioned below,
import React from "react";
export default function UploadImage() {
const [imgsSrc, setImgsSrc] = React.useState([]);
const uploadToClient = (e) => {
for (const file of e.target.files) {
setImgsSrc((imgs) => [...imgs, file]);
}
};
const uploadToServer = async () => {
let formData = new FormData();
for (const file of imgsSrc) {
formData.append('image', file, file.name)
}
const response = await fetch("/api/upload", {
method: "POST",
body: formData
});
};
return(
<div>
<h3>Upload photos</h3>
<input onChange={uploadToClient} type="file" name="file" multiple />
<div>
{imgsSrc.map((link) => (
<img src={link} style={{ width: 200, height: 200 }} />
))}
</div>
<button type="submit" onClick={uploadToServer}>Send to server</button>
</div>
)
}
When uploading images I am getting 'error - unhandledRejection: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined'.
Any suggestion would be highly appreciated!
I am experimenting with React and I am trying to upload multiple images to my API in flask for saving. I was able to figure out how to upload a single file, but am struggling to convert to multiple. Here is the code for single upload.
FLASK
#app.route('/upload', methods={"POST"})
def upload_file():
file = request.files.getlist("file")
print(file)
response="Whatever you wish to return"
return response
REACT
export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
};
this.handleUploadImage = this.handleUploadImage.bind(this);
}
handleUploadImage(ev) {
ev.preventDefault();
const data = new FormData();
data.append('file', this.uploadInput.files[0]);
fetch('http://localhost:5000/upload', {
method: 'POST',
body: data,
}).then((response) => {
response.json().then((body) => {
});
});
}
render() {
return (
<form onSubmit={this.handleUploadImage}>
<div>
<input ref={(ref) => { this.uploadInput = ref; }} type="file" />
</div>
<br />
<div>
<button>Upload</button>
</div>
</form>
);
}
}
You could set the multiple property on your file input so you can select multiple files:
<input
ref={(ref) => {
this.uploadInput = ref;
}}
type="file"
multiple
/>
Then you could change your handleUploadImage function so it sends all files selected:
handleUploadImage(ev) {
ev.preventDefault();
const data = new FormData();
for (let i = 0; i < this.uploadInput.files.length; i++) {
data.append("file", this.uploadInput.files[i]);
}
fetch("http://localhost:5000/upload", {
method: "POST",
body: data,
}).then((response) => {
response.json().then((res) => {
console.log(res);
});
});
}
What i am trying to achieve is send a post request from my frontend to back end using axios.This post request has a csv file in its body.
Using postman : postman request
My code is this:
import React, { Component } from 'react';
import axios from 'axios'
class SessionsUpdate extends Component {
state = {
selectedFile: null
}
handleSubmit = async () => {
let formData = new FormData();
formData.append('file', this.state.selectedFile);
await axios.post(
'https://localhost:8765/...',
formData,
{ headers: { 'x-observatory-auth': localStorage.getItem("token"), 'Content-Type': 'multipart/form-data' } }
)
console.log("log") //this is not printed here
}
onFileChange = event => {
this.setState({ selectedFile: event.target.files[0] });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>
<h1>Choose a file to store</h1>
</div>
<div>
<fieldset>
<input type="file" accept=".csv" onChange={this.onFileChange} />
</fieldset>
</div>
<input type="submit" value="Submit" />
</form>
);
}
}
export default SessionsUpdate;
So the HTML part creates a simple GUI to select a csv file from local storage and what i want is to pass this csv file to my post request.After searching online the main way i found to do this is by using formdata but it does not work on my case and i have been stuck in this for quite a while.This requests works fine on postman though.
Any ideas about what i am missing?
I'm generating S3 presigned url for uploading the file from local. On frontend I'm using React.
I get the presigned URL using API call and then trying to upload the file using axios but it gives 403 (Forbidden).
If I use the same presigned url using 'curl' then it works fine and the same file is uploaded on S3.
s3.py - For generating the pre-signed url:
class S3Controller:
def __init__(self, client=None, bucket=None):
self.client = client
self.bucket = bucket
def signed_url(self, filename):
filename = filename.replace('/', '-').replace(' ', '-')
date = datetime.now()
key = f"audio/{date.year}/{date.month}/{date.day}/{filename}"
url = self.client.generate_presigned_url(
ClientMethod='put_object',
ExpiresIn=3600,
Params={
'Bucket': self.bucket,
'Key': key,
}
)
return url
Component in react for uploading the file:
import React, { Component, Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { S3SignedUrl } from '../query';
import { withApollo } from 'react-apollo';
import AudioUploadButton from '../components/AudioUploadButton';
import axios from 'axios';
class UpdateAudio extends Component {
constructor(props) {
super(props);
this.site = "5d517862-0630-431c-94b1-bf34de6bfd8b"
this.state = {
audioSelected: {},
audioLoaded: 0
}
this.onSelect = this.onSelect.bind(this);
this.onUpload = this.onUpload.bind(this);
}
onSelect = (event) => {
const fileInfo = event.target.files[0];
this.setState({audioSelected: fileInfo});
}
onUpload = async () => {
let resp = await this.props.client.query({ query: S3SignedUrl, variables: {filename: this.state.audioSelected.name}});
let { data } = resp;
let endpoint = data.s3SignedUrl.url;
axios.put(endpoint, this.state.audioSelected, {
onUploadProgress: ProgressEvent => {
this.setState({
audioLoaded: (ProgressEvent.loaded / ProgressEvent.total*100)
})
}
})
.then(res => {
console.log(res);
})
}
render() {
return (
<Fragment>
<AudioUploadButton onSelect={this.onSelect} onUpload={this.onUpload} audioSelected={this.state.audioSelected} audioLoaded={this.state.audioLoaded} />
</Fragment>
)
}
}
UpdateAudio = withRouter(UpdateAudio)
export default withApollo(UpdateAudio);
AudioUploadButton.js
import React from 'react';
import { Grid, Button, Typography, Fab } from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import CloudUploadIcon from '#material-ui/icons/CloudUpload';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
},
input: {
display: 'none',
},
fab: {
margin: theme.spacing.unit,
},
});
class AudioUploadButton extends React.Component {
render() {
let { classes } = this.props;
let { name, size } = this.props.audioSelected;
let loaded = this.props.audioLoaded;
return (
<Grid container spacing={8} >
<Grid item md={2} xs={12}>
<input
accept="audio/*"
className={classes.input}
id="contained-button-file"
type="file"
onChange = {this.props.onSelect}
/>
<label htmlFor="contained-button-file">
<Button variant="contained" component="span" className={classes.button}>Select</Button>
</label>
</Grid>
<Grid item md={1} xs={12}>
<Fab color="secondary" size='medium' onClick={this.props.onUpload}>
<CloudUploadIcon />
</Fab>
</Grid>
<Grid item md={9} xs={12}>
<Typography variant='caption' gutterBottom>{name} {size} {loaded}</Typography>
</Grid>
</Grid>
)
}
}
export default withStyles(styles)(AudioUploadButton);
Curl works without any issue:
curl -X PUT --upload-file 1.jpg https://s3.amazonaws.com/bucket-name/filepath.jpg?AWSAccessKeyId=xyz&Signature=Vql3Bnkb7H847Cr4vtw5gbi%2F%2Bs%3D&Expires=1546873244
Thanks for the help.
Creating aws presigned url in python
import boto3
import haslib
import json
if "AWS_S3_ENDPOINT_URL" in os.environ:
s3_client = boto3.client("s3", endpoint_url=os.environ["AWS_S3_ENDPOINT_URL"])
else:
s3_client = boto3.client("s3")
def resolve_create_presigned_url_for_file_upload(data, info):
object_name = hashlib.sha256(os.urandom(1024)).hexdigest()
bucket_name = "my_bucket_name"
expiration = 60 * 10 # 600 seconds
s3_client = boto3.client("s3")
try:
response = s3_client.generate_presigned_post(
bucket_name, object_name, Fields=None, Conditions=None, ExpiresIn=expiration
)
except ClientError as e:
logging.error(e)
return None
if response is None:
exit(1)
return {"url": response["url"], "fields": json.dumps(response["fields"])}
Uploading the file in javascript using the presigned url
// here preSignedPostData is the data returned from the function above
const uploadFileToS3 = (presignedPostData, file) => {
// create a form obj
const formData = new FormData();
// append the fields in presignedPostData in formData
Object.keys(presignedPostData.fields).forEach(key => {
formData.append(key, presignedPostData.fields[key]);
});
// append the file
formData.append("file", file.src);
// post the data on the s3 url
axios.post(presignedPostData.url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
};
I am using Rails API in the backend and ReactJS in frontend. I am also using carrier-wave for processing my image in the backend. I tried to upload my image and send it to the backend. The image files are being captured in the frontend by using simple HTML uploader.
This is my uploader.jsx
import React from 'react'
import axios, { post } from 'axios';
import { PORT } from '../constant.js';
class PrescriptionNew extends React.Component {
constructor(props) {
super(props);
this.state ={
file: null
}
this.onFormSubmit = this.onFormSubmit.bind(this)
this.onChange = this.onChange.bind(this)
this.fileUpload = this.fileUpload.bind(this)
}
onFormSubmit(e){
e.preventDefault() // Stop form submit
this.fileUpload(this.state.file).then(response => {
console.log('response', response.data)
})
}
onChange(e) {
this.setState({file: e.target.files[0]})
}
fileUpload(file){
const url = `${PORT}/prescriptions`;
var FormData = require('form-data');
const formData = new FormData();
formData.append('file',file)
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post(url,formData, config)
}
render() {
if (this.state.file !== null) {
console.log('this.file', this.state.file.name)
}
return (
<form onSubmit={this.onFormSubmit}>
<h1>File Upload</h1>
<input type="file" onChange={this.onChange} />
<button type="submit">Upload</button>
</form>
)
}
}
export default PrescriptionNew;
The above code is an exact replica of this solution.I have used formData and axios in order to PUSH the data to the backend. But the datas are stored as a "nil" values in the database.
console.log(formData) gives an empty hash.
My Controller code in backend side is
def create
#prescription = Prescription.new(prescription_params)
#prescription.user = current_user
unless #prescription.save
render json: { status: 200, errors: #prescription.errors } and return
else
render :show
end
end
I have also refered many solution given in this portal. But still , cannot able to study whats going wrong in my code.
The simple solution is to populate FormData. Returning an empty FormData will store 'nil' values in the database.
The perfect soution is
class PrescriptionNew extends Component {
upload_image(){
var element = document.getElementById("image-form")
var formData = new FormData(element);
const url = `${PORT}/prescriptions`
fetch(url,{
method: "POST",
body: formData
})
.then(response => response.json)
}
after_submit(event){
event.preventDefault();
}
render(){
return(
<div>
<h3 className="index-title">Upload Your Prescription</h3>
<form onSubmit={event => this.after_submit(event)} id="image-form" encType='multipart/form-data'>
<input type="file" name="image_path"/>
<button onClick={() => this.upload_image()}>Upload</button>
</form>
</div>
)
}
}
export default PrescriptionNew;