I am trying to upload the image in react, I used the extension ** react-image-upload**.
I used this code to show images on a webpage
import React from 'react';
import ImageUploader from 'react-images-upload';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { pictures: [] };
this.onDrop = this.onDrop.bind(this);
}
onDrop(pictureFiles, pictureDataURLs) {
this.setState({
pictures: pictureFiles
});
}
render() {
return (
<ImageUploader
withIcon={true}
buttonText='Choose images'
onChange={this.onDrop}
imgExtension={['.jpg', '.gif', '.png', '.gif']}
maxFileSize={5242880}
/>
);
}
}
Now The image is showing on the webpage but I don't know how to upload it and save it to a folder I am trying to use this code.
This line is for onChange
onDrop(pictureFiles, pictureDataURLs) {
this.setState({
picture: pictureFiles
});
}
This line is uploading the image, but I don't know how it will function and if I need to add the backend.
uploadHandler = () => {
const formData = new FormData();
formData.append('file', this.state.picture);
console.log()
let context = 'profile';
service.uploadImage(formData,
this.state.picture,
context, (res) => {
if (res.status === "ERROR") {
console.log("ERROR: ", res);
stopLoading();
}
});
}
And the upload function -
export const uploadImage = (file, fileName, context, onDone) => {
console.log(fileName)
post(encodeURI("/api/upload-files/" + fileName + "/" + context), file, (res) => {
onDone(res.data);
});
};
And the backend code, I am using FastAPI- Python for it-
#router.post("/upload-files")
async def image(image: UploadFile = File(...)):
print(image.filename)
return {"filename": image.filename}
I checked the values and the results are right on backend but on webpage it is showing 422: preprocessing error.
Please give some idea what's the process or some hint so I can complete the code.
Related
I'm using expo/react-native, developing on MacOS, testing on the iOS emulator. Following various examples I keep getting 400s (invalid url, missing file parameter etc...) when I POST a file selected from the photo library to Cloudinary. Unfortunately Cloudinary doesn't have very good documentation for react-native.
I also want to display a progress indicator so using fetch() is not an option.
I'm doing the uploads directly from the mobile app rather than passing through my server which is why Cloudinary's nodeJS docs don't apply. For security these upload requests will eventually be signed.
This took way longer to figure out than I liked so I'm posting the solution here. The real key is that Cloudinary expects image data to be provided as base64 from mobile whereas from the web a local file URL was sufficient. Fortunately the base64 data is available directly from the expo-image-picker component but it still must be formatted as a data uri to be uploaded.
import React, { useEffect, useState } from 'react';
import * as ImagePicker from 'expo-image-picker';
import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types';
import { Platform, TouchableOpacity } from 'react-native';
import { CloudinaryImage } from './CloudinaryImage';
export const MyComponent = () => {
const [cloudinaryId, setCloudinaryId] = useState('');
const [progress, setProgress] = useState(100);
useEffect(() => {
(async () => {
if (Platform.OS !== 'web') {
const { status } =
await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted')
alert('Sorry, we need camera roll permissions to make this work!');
}
})();
}, []);
return (progress === 100) ?
(
<TouchableOpacity onPress={pickImage}>
<CloudinaryImage cloudinaryId={cloudinaryId} />
</TouchableOpacity>
) : ( ... a progress indicator of your choosing );
The pickImage function follows. This runs when the user touches the TouchableOpacity component.
const pickImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
base64: true, // this must be set for base64 to be returned!
});
if (!result.cancelled) {
const { uri, base64 } = result;
cloudinaryUpload({ image: { uri, base64 }, setProgress, setCloudinaryId });
}
};
The cloudinaryUpload function follows. This uses XMLHttpRequest instead of fetch in order to support progress.
const cloudName = 'yourCloud';
const uploadPreset = 'yourPreset';
const url = `https://api.cloudinary.com/v1_1/${cloudName}/upload`;
export const cloudinaryUpload = async ({ image, setProgress, setCloudinaryId }) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener('progress', (e) => {
const progress = Math.round((e.loaded * 100.0) / e.total);
setProgress(progress);
});
// handle successful upload
xhr.onreadystatechange = (e) => {
if (xhr.readyState == 4 && xhr.status == 200) {
const response = JSON.parse(xhr.responseText);
setProgress(100);
setCloudinaryId(response.public_id);
}
};
xhr.addEventListener('error', (e) => console.error(e));
const fd = new FormData();
const data = `data:;base64,${image.base64}`; // the critical step!
fd.append('file', data);
fd.append('upload_preset', uploadPreset);
fd.append('tags', 'your tags');
setProgress(0);
xhr.send(fd);
};
<CloudinaryImage /> is just a component that renders a Cloudinary image based on its ID. The source to that component is the answer to a different question.
I have yet to try this on Android.
I'm using React-dropzone-uploader
const [state,setState] = useState({document_blob_ids: [] })
const MyUploader = () => {
const getUploadParams = ({ meta }) => { // specify upload params and url for your files
return { url: '/v1/file_uploads/' }
}
const handleChangeStatus = ({ meta, file,xhr }, status) => { // called every time a file's `status` changes
console.log("handleStatus",status, meta, file)
if(status == "done") {
var json = JSON.parse(xhr.response)
var arr_blob_ids = state.documents_blob_ids.slice()
console.log("id added",json.blob.id)
if (json.blob.id){
arr_blob_ids.push(json.blob.id)
setState({...state,documents_blob_ids: arr_blob_ids})
}
}
else if(status == "removed") {
var json = JSON.parse(xhr.response)
var arr_blob_ids = state.documents_blob_ids.slice()
console.log("id removed",json.blob.id)
if (json.blob.id){
arr_blob_ids.filter( v => v!= json.blob.id)
setState({...state,documents_blob_ids: arr_blob_ids})
}
}
}
const handleSubmit = (files, allFiles) => { // receives array of files that are done uploading when submit button is clicked
console.log(files.map(f => f.meta))
allFiles.forEach(f => f.remove())
}
return (
<Dropzone
getUploadParams={getUploadParams}
onChangeStatus={(handleChangeStatus}
onSubmit={handleSubmit}
accept="image/*"
submitButtonContent = {null}
SubmitButtonComponent = {null}
/>
)
}
using the react-dropzone-uploader I'm trying to upload multiple files , But After a Successful upload of file since i'm updating the state , the component is rerendering which makes the loss of preview of uploaded files..
So i'm looking for an alternate solution, like storing all the id's in a global array and on clicking the submit button I can update it to state.
can someone guide me how to declare and use global array or any other alternate solution that solves my problems..
I am attempting to upload an image to active storage in my Rails backend. I currently have the backend setup correctly but I am struggling to understand FormData. Here is what I have.
The on change function is listening for a change in the input which is of type 'file'. When I add the photo to the input I can get the info from e.target... However I don't know what to add as the URI here. The e.target.value here is a uri that is protected. So I'm confused about how this is supposed to work:
By the way, setFile is just setting 'file' to that object. I am using react.
const onChange = (e) => {
console.log(e.target.files)
setFile({
uri: e.target.value,
name: e.target.files[0].name,
type: e.target.files[0].type
})
}
const onSubmit = () => {
console.log(file)
let imageURI = file.uri
let formdata = new FormData();
formdata.append("image", { uri: imageURI, name: `${file.name}`, type: `${file.type}` })
formdata.append("image", file)
requests.postImageToCurriculum(66, formdata)
}
if you are using Active storage, use their js package
import { DirectUpload } from "#rails/activestorage"
class Uploader {
constructor(file, url) {
this.upload = new DirectUpload(this.file, this.url, this)
}
upload(file) {
this.upload.create((error, blob) => {
if (error) {
// Handle the error
} else {
// Add an appropriately-named hidden input to the form
// with a value of blob.signed_id
}
})
}
directUploadWillStoreFileWithXHR(request) {
request.upload.addEventListener("progress",
event => this.directUploadDidProgress(event))
}
directUploadDidProgress(event) {
// Use event.loaded and event.total to update the progress bar
}
}
link here
I have a big application in React-Native and I have a lot of duplicate functions in the code.
So I created a file called function.js which could contain all my duplicate functions. Like queries on the local database, remote data base...
So I pretty much got the job done. However I have a problem.
This two functions must be used one after the other.
The first one does an update of the state to get a user id from local database.
The second one, asks information from the remote database with the user id retrieved by the first function in parameters.
When both calls are in the componentdidmount element, unfortunately it doesn't work !!
The update time of the state by the first function is too slow compared to the execution of the second function.
The second function gets an "undefined" parameter when it is executed. because the state is not updated by the first function for the moment.
If I put the second function in componentDidUpdate() it works but it runs in a loop so it's not a solution either.
I also don't want to trigger the execution of the second function at the end of the first one in the external file. It would make the functions not autonomous from each other.
And I think that the solution of a timeout() is not very good either, even if we could work with it.
Example code :
It's the content of my App.js file that imports the Function.js file containing all my functions
import React, { Component } from 'react';
import { fetchUser, prepareUserData } from 'bitcoin/Functions/Function'
export default class Profile extends Component {
state = {
user_id: "",
}
componentDidMount() {
fetchUser.call(this);
prepareUserData.call(this, this.state.user_id)
}
render{
return (<View></View>)
}
This is the content of my Function.js file which contains functions that are duplicated in my application.
import * as SQLite from "expo-sqlite";
import axios from "axios";
const db = SQLite.openDatabase("db.db");
/* #############################################################################
User data retrieval function in the local database
##############################################################################*/
export function fetchUser () {
let query = "select * from ?";
let params = [];
db.transaction(tx => {
tx.executeSql(
query,
params,
(_, { rows: { _array } }) => {
this.setState({user_id: _array[0].user_id})
},
function(tx, err) {
console.log("Erreur" + err);
}
);
});
}
/* #############################################################################
User data retrieval function in the remote database
##############################################################################*/
export function prepareUserData(userID) {
let userConnect = new FormData();
userConnect.append("id", userID);
console.log(userConnect)
const url =
"https://*************/rest_api/React_native_api/appmobile_profile";
axios
.post(url, userConnect)
.then(res => {
console.log(res.data)
if(res.status === 200){
this.setState(
{
user_pseudo: res.data.pseudo,
[ ... ]
user_lastName: res.data.last_name,
},);
}
})
.catch(err => {
console.log("Erreuur", err);
});
}
I've tried a lot of things with async componentDidMount(), await myfunc(), creating asynchronous functions in my function file ...
But I can't find solutions. I could do otherwise but I find the problem really interesting.
I think there is a way to optimize my use of react native.
Thank you for your various feedbacks. Have a nice day.
Okay, so I've come up with a effective solution to this problem.
I don't know much about promise and yet it seems to be a key for a lot element in react-native and javascript in general.
I'll share my code. Hopefully it can be useful to someone in the future! thank you for your prompt return and see you soon!
To make it work I used:
The Promise Object new Promise(function(resolve,reject)
Creation of a dynamic SQL query by adding parameters when calling the function.
File App.js
import React, { Component } from 'react';
import { selectFromTable, prepareUserData } from 'bitcoin/Functions/Function'
export default class Profile extends Component {
state = {
user_data: "",
}
componentDidMount() {
selectFromTable('user', ['*']).then((result) => {
this.setState({ user_data: result.rows._array[0] },() =>
{prepareUserData.call(this, this.state.user_data.user_id)})
})
}
render{
return (<View></View>)
}
File Function.js
export function selectFromTable(table_name, selected_columns, conditions, callback) {
return new Promise(function(resolve,reject) {
let db_cmd = `SELECT ${selected_columns} FROM ${table_name}`;
db.transaction((tx, err) => {
tx.executeSql(db_cmd, [], (tx, res) => {
resolve(res);
},
function(tx, err) {
console.log("Error" + err);
}
);
});
});
}
export function prepareUserData(userID) {
let userConnect = new FormData();
userConnect.append("id", userID);
const url =
"https://**********/**********/appmobile_profile";
axios
.post(url, userConnect)
.then(res => {
console.log(res.data)
if(res.status === 200){
this.setState(
{
user_pseudo: res.data.pseudo,
user_cellphone: res.data.cellphone,
user_callingCode: res.data.calling_code,
});
};
})
.catch(err => {
console.log("Error", err);
});
}
I'm creating an application using ionic 3. The app is supposed to do the following: take an image from external camera (it will return it as file URL), store it in local DB using sqllite as base64.
This is my attempt
takeImage(){
const fileTransfer: FileTransferObject = this.transfer.create();
this.takePicture().subscribe(data => {
this.wait(7000);
this.listFiles().subscribe(data => {
const url = data['results'].entries[0].fileUrl;
fileTransfer.download(url, this.file.dataDirectory + data['results'].entries[0].name).then((entry) => {
this.room.img = entry.toURL();
this.base64.encodeFile( entry.toURL()).then((base64File: string) => {
this.room.img = base64File;},
(err) => {
console.log(err);
});
}, (error) => {});
},
err => {console.log(err);});
},
err => {console.log(err);});
}
I have tried to display the base64File but it is not working.
To display a base64 image you need to import DomSanitizer in component and create the instance variable in the constructor below is the code
constructor(public _DomSanitizer: DomSanitizer)
<img class="one-image" *ngFor="let image of item; let i= index" style="height:120px;" [src]="_DomSanitizer.bypassSecurityTrustUrl(image.imgUrl)" #images (click)="imageOption(i,images,image.data,image.ext)"/>