MERN+ Cloudinary: Unsupported source URL - reactjs

I'm trying to upload file to cloudinary. Here is part of my react component
...
addItem() {
...
let file = this.fileInput.value;
keywords !== "" && this.props.onAddItem(keywords, place, image);
...
}
render() {
return (
....
<Input
type="file"
innerRef={(input) => {this.fileInput = input}}
name="image"
id="image"
placeholder=""/>
)
}
Here is action file:
export function onAddItem(keywords, place, file, id, isChangebale = false) {
return (dispatch) => {
axios.all([
axios.post('https://api.cloudinary.com/v1_1/myservername/image/upload',
{upload_preset: "mypresetname", file: file}),
axios.post('http://localhost:3001/api/items/', { keywords, place, id, isChangebale })
])
.then(axios.spread((cloudinaryRes, localRes) => {
console.log(cloudinaryRes, localRes);
}))
I receive error xhr.js:178 POST https://api.cloudinary.com/v1_1/testovich/image/upload 400 (Bad Request) and in response headers "X-Cld-Error: Unsupported source URL: C:\fakepath\2017-12-07_19-06-445.png"
When I test using postman I have correct response.
So it looks like I do something wrong when pass file from rect component to action file. How to pass correct path/file to cloudinary?

There were two mistakes:
1. in react component there should be
let file = this.fileInput.files[0];//I upload only one file
instead of
let file = this.fileInput.value;
in action file
export function onAddItem(keywords, place, image, id, isChangebale = false) {
const formData = new FormData();
formData.append("file", image);
formData.append("upload_preset", "mypresetname");
return (dispatch) => {
axios.all([
// AJAX upload request using Axios )
axios.post('https://api.cloudinary.com/v1_1/myservername/image/upload',
formData,
instead of:
export function onAddItem(keywords, place, file, id, isChangebale = false) {
return (dispatch) => {
axios.all([
axios.post('https://api.cloudinary.com/v1_1/myservername/image/upload',
{upload_preset: "mypresetname", file: file}),

Convert the image to a base64 like const base64Img = data:image/jpg;base64,${file.data};
The file.data represents the data property from response from image picker.
Then I passed the base64Img to data like
return RNFetchBlob.fetch('POST', apiUrl, headerProps, [ { name: 'file', fileName: file.fileName, type: file.type, data: base64Img } ]);
Hope it helps.

Related

trying to select a .csv file from the file input and pass it to the backend and read it from the back end using remix run

I am trying to select a CSV file with some columns and data using file input. This is the content of the CSV
id,level,cvss,title,Vulnerability,Solution,reference, 963,LOW,7.8,Title - 963,Vulnerability - 963,Solution - 963,reference - 963, 964,CRITICAL,4.6,Title - 964,Vulnerability - 964,Solution - 964,reference - 964, 965,INFO,0,Title - 965,Vulnerability - 965,Solution - 965,reference - 965, 966,CRITICAL,10,Title - 966,Vulnerability - 966,Solution - 966,reference - 966, 967,HIGH,7.5,Title - 967,Vulnerability - 967,Solution - 967,reference - 967, 968,HIGH,5,Title - 968,Vulnerability - 968,Solution - 968,reference - 968, 969,MEDIUM,7.5,Title - 969,Vulnerability - 969,Solution - 969,reference - 969,
This is the code for the UI
import { Form } from "#remix-run/react";
import type { ActionArgs, UploadHandler } from "#remix-run/server-runtime";
import {
composeUploadHandlers,
parseMultipartFormData,
} from "#remix-run/server-runtime/dist/formData";
import { createMemoryUploadHandler } from "#remix-run/server-runtime/dist/upload/memoryUploadHandler";
import { csvUploadHandler } from "~/models/code.server";
export async function action({ request, params }: ActionArgs) {
const uploadHandler: UploadHandler = composeUploadHandlers(
csvUploadHandler,
createMemoryUploadHandler()
);
const formData = await parseMultipartFormData(request, uploadHandler);
const selected_csv = formData.get("selected_csv");
console.log("========== selected csv file: ", selected_csv);
return selected_csv;
}
export default function codeListImport() {
return (
<div style={{ textAlign: "center" }}>
<h1 style={{ marginBottom: 10 }}>UPDATE CODE LIST</h1>
<Form method="post" encType="multipart/form-data">
<input
type="file"
accept=".csv"
name="selected_csv"
/>
<button type="submit" className="btn btn-sm">
UPLOAD CSV
</button>
</Form>
</div>
);
}
This is the code.server.ts file
export const csvUploadHandler: UploadHandler = async ({
name,
filename,
data,
contentType,
}) => {
if (name !== "selected_csv") {
return undefined;
}
console.log("===== file name", filename);
console.log("===== data", data);
console.log("===== content type", contentType);
};
I was trying to get the content of the CSV file using data given by the uploadHandler. I get the correct name of the HTML input element, file name as well as the content type. But when I console log data it shows me this on the log:
What I clearly do not get is how to decode this object
I have gone through multiple tutorials and many stackoverflow posts but I am still having a hard time understanding what exactly is the Symbol.asyncIterator. I am a newbie for ES6. Please help me on this matter
In your UploadHandler, the parameter is type UploadHandlerPart
export type UploadHandlerPart = {
name: string;
filename?: string;
contentType: string;
data: AsyncIterable<Uint8Array>;
};
So data is not the CSV string, it's an AsyncIterable of bytes. You need to convert this to a string and then return that from your handler.
export const csvUploadHandler: UploadHandler = async ({
name,
filename,
data,
contentType,
}) => {
if (name !== 'selected_csv') {
return undefined;
}
// get data in chunks
let chunks = [];
for await (let chunk of data) {
chunks.push(chunk);
}
// convert chunks to string using Blob()
return await new Blob(chunks, { type: contentType }).text();
};
I have an example here: https://stackblitz.com/edit/node-ubpgp5?file=app%2Froutes%2Findex.tsx
Since you're not really doing anything in your csvUploadHandler, you can simply use the existing memoryUploadHandler(). Your csv file will be in formData as a File type.
const uploadHandler = createMemoryUploadHandler();
const formData = await parseMultipartFormData(request, uploadHandler);
const csvFile = formData.get('selected_csv') as File;
const csv = await csvFile.text();

Convert uri to file react native js

Above is the request that I am trying to make in react native.
But I have URI
How shall I convert this to a file while sending to backend(PHP) server
This's my data to be sent in body:
const profileImageToBeSentToUpdate = {
data: {
id: authContext.user.id,
token: authContext.user.oauth_token.access_token,
profile_image: source.uri //Need help here, need to convert uri to file in order to send to php
},
type: 'POST',
url: 'update_profile_image',
success: profileImageUpdateSuccess,
error: profileImageUpdateError
};
authContext.setLoader();
console.log('***********profile data binded: ', profileImageToBeSentToUpdate);
NetworkAdaptation.postData(profileImageToBeSentToUpdate);
Now, backend (PHP) wants file format, I have URI format in react
React and React Native have no idea about what file system is. So they cannot make a file.
A workaround could be download a new file by using this code:
downloadprofileImageFile = () => {
const element = document.createElement("a");
const file = new Blob([source.uri], {type: 'text/plain'});
element.href = URL.createObjectURL(file);
element.download = "profile_image.txt";
document.body.appendChild(element);
element.click();
}
Then, pass the file to your profileImageToBeSentToUpdate function.
use rn-fetch-blob
import { Platform } from 'react-native';
const postImage = (localImageUri, remoteUrl) =>
RNFetchBlob.fetch(
'POST',
remoteUrl,
{ 'Content-Type': 'multipart/form-data' },
[
{
name: 'myimage',
filename: 'myimage',
type: 'image/jpeg',
data: RNFetchBlob.wrap(Platform.OS === 'ios' ? localImageUri.replace('file://', '') : localImageUri),
},
],
).then(response => {
// do something
}).catch(error => {
// do something
});
I used image picker and from imageResponse during showImagePicker method got the following details and used it in data to send
profile_image: { uri: imageResponse.uri, name: imageResponse.fileName, type: imageResponse.type }

Uploading images from react with laravel api

I'm having trouble uploading files from a react input using a laravel API.
I'm working with react-hook-form.
My form and onSave are as follows
const onSave = data => {
// data.picture = imgs; here I tried changing the picture to event.target.files from the file input, didn't work either.
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
axios
.post(`/api/products/store`, data, {})
.then(res => {
console.log(res);
})
.catch(err => console.log(err));
};
return (
<form onSubmit={handleSubmit(onSave)} encType="multipart/form-data">
<input
type="file"
name="picture[]"
label="Product Picture"
onChange={handlePicInput}
className={classes.inputFile}
multiple
/>
//other inputs
</form>
);
my post request leads to this controller method
public function store(Request $request)
{
$imageNames = '';
$pictures = (object) $request->file('picture');
//$pictures = $request->allFiles();
//$pictures = (object) $request->file('picture[]');
//$pictures = (object) $request->files;
foreach ($pictures as $key => $picture) {
/*WHEN I'M USING POSTMAN OR INSOMNIA,
this foreach loop is accessed but
the react form just skips the foreach completely */
$imageNames = $imageNames . $picture->store('product_pictures', 'public') . ',';
}
$product = Product::create([
'name' => $request->name,
'prices_amountmax' => $request->prices_amountmax,
'prices_amountmin' => $request->prices_amountmax,
'brand' => $request->brand,
'manufacturer' => $request->manufacturer,
'weight' => $request->weight,
'category_id' => $request->category_id,
'stock' => $request->stock,
'imageurls' => $imageNames
]);
$product->save();
}
To sum up, I tested uploading images with postman, it works just fine, so the problem must be in the react form?
Thank you for any kind of help
To upload images using js you can use FormData. I can't see your handlePicInput method to understand how input change is handled, but may be this snippet can help you to understand what to do further.
function handlePicInput(event){
let images = event.target.files
let fd = new FormData()
fd.append("images", images);
}
Then you can append to fd your other values and send via axios
axios.post(`/api/products/store`, fd)
Again, where to place the code and how to handle other inputs you have to manage by yourself, or provide more data
Try sending it as formData, with multiple files:
const onSave = data => {
const formData = new FormData();
for (let i in data) {
if(i === 'picture[]'){
for(let file of data[i]){
formData.append('picture',file);
}
}else{
formData.append(i, data[i])
}
}
// data.picture = imgs; here I tried changing the picture to event.target.files from the file input, didn't work either.
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
axios
.post(`/api/products/store`, formData, {})
.then(res => {
console.log(res);
})
.catch(err => console.log(err));
};
I tested it with my Node/Express backend and it seems to work. "picture" will be an array of files. If your php backend doesn't recognize this correctly, try changing the formData.append('picture',file) to formData.append('picture[]',file), but then you'll also need to change the name in your php.

React Laravel - File showing in state but not in controller

My project has React as frontend and Laravel as backend.
I am trying to upload a file and the details of the file appear in state while uploading but not in the controller and hence not able to upload it in folder.
Component has
<div className="form-group col-sm-4">
<div className="imgPreview">{$imagePreview}</div>
<label>Profile Pic</label>
<input className="form-control" type="file" name="profile_pic" onChange={(e)=>this._handleImageChange(e)}/>
</div>
further codes are:
_handleImageChange(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
fileselected: file,
profile_pic: file.name,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file)
}
Data gets uploaded though axios
submitHandler = e =>{
e.preventDefault()
this.props.drprofilecreate(this.state)
}
export const drprofilecreate = (data) => dispatch =>{
console.log("Coming form profile create action ")
console.log(data)
return axios.post('/api/v1/drProfileCreate', data)
.then( res =>res.data )
.then(drprofile =>
dispatch({
type: DRPROFILECREATE,
payload: drprofile,
}),
)
}
When I view the data being uploaded, it shows the file with its details like name, size etc. But the same does not come in the controller. It shows a blank array.
public function drProfileCreate(Request $request){
$data = $request->all();
$response = [
'success' =>true,
'datax' =>'Dr Profile uploaded in Contorller. Check',
'data' => $data
];
return response()->json($response, 201);
}
Hence Iam not able to upload the image. Help please. Appreciated

Confused on blob:url and converting it to base64 in react-dropzone

I am using the package react-dropzone (https://github.com/okonet/react-dropzone) to get images from the user. The user uploads their image and everything is fine, but Im only getting something like "blob:http//blahblah" from it and I need the image to be in base64 png.
my dropzone component:
<Dropzone ref="dropzone" multiple={false} onDrop={this.onDrop.bind(this)} >
{this.state.files ?<img className="img-responsive" src={this.state.files[0].preview}></img>
: <div>Upload Photo</div> }
</Dropzone>
and the drop function that gets the blob url :
onDrop (files ) {
if ( files.length === 0 ) {
alert("upload img please")
return;
}
console.log('Received files: ', files);
this.setState({files:files})
var blobURL = files[0].preview
var reader = new FileReader();
reader.readAsDataURL(blobURL)
}
I would get an error :Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.
I think it is because im trying to pass in an object-url that points to a blob, but where would I be able to get blob so I can convert to base64?
I would suggest to use a promise to get the result of async convertion by FileReader.readAsDataURL method. Here's the sample how it can be done:
const promise = new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(files[0])
reader.onload = () => {
if (!!reader.result) {
resolve(reader.result)
}
else {
reject(Error("Failed converting to base64"))
}
}
})
promise.then(result => {
// dispatch or do whatever you need with result
}, err => {
console.log(err)
})

Resources