How to display image from AWS s3 using spring boot and react? - reactjs

How to display images from amazon s3 bucket in react?
Images are not being displayed using amazon s3,spring boot 2.5.3,react.
I have tested the end points in postman, it works.
React: Using Axios to connect to backend and MyDropZone
Using axios to make a post request, I can confirm that images are being saved in s3.
This is the reponse after making a post request, for the first 2 user.
[
{
"userId":"565c6cbe-7833-482d-b0d4-2edcd7fc6163",
"userName":"John",
"imageUrl":"pexels-photo-3147528.jpeg"
},
{
"userId":"3c776990-38e8-4de4-b7c6-7875c0ebb20f",
"userName":"Anthony",
"imageUrl":"pexels-photo-3147528.jpeg"
},
{
"userId":"bcac9cf2-5508-4996-953e-b18afe866581",
"userName":"Peter",
"imageUrl":null
}
]
React:
import './App.css';
import axios from 'axios';
import { useState, useEffect,useCallback } from 'react';
import {useDropzone} from 'react-dropzone'
//
const UserProfiles = () => {
const [userProfiles,setUserProfiles]=useState([])
const fetchUserProfiles=() => {
axios.get('http://localhost:5000/api/v1/users').then((response) => {
console.log(response.data)
setUserProfiles(response.data)
})
}
useEffect(() => {
fetchUserProfiles();
}, [])
return userProfiles.map((profile,index) => {
return (
<div key={index}>
<MyDropZone userId={profile.userId}></MyDropZone>
<h3>{profile.userId}</h3>
{
profile.userId ? (<img src={`http://localhost:5000/api/v1/users/${profile.userId}/image/download`} /> ) : <h5>No profile Image Uploaded</h5>
}
</div>
);
})
}
function MyDropZone({userId}) {
const onDrop = useCallback(acceptedFiles => {
// Do something with the files
console.log(acceptedFiles[0])
const file=acceptedFiles[0]
//Form-data
const formData = new FormData()
formData.append('file', file)
//Make a post req
axios.post(`http://localhost:5000/api/v1/users/${userId}/image/upload`, formData, {
headers: {
'Content-Type':'multipart/form-data'
}
}).then((response) => {
console.log(response)
console.log("Uploaded")
}).catch((error) => {
console.log(error)
})
}, [])
const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
{
isDragActive ?
<p>Drop the files here ...</p> :
<p>Drag 'n' drop some files here, or click to select files</p>
}
</div>
)
}
function App() {
return (
<div className="App">
<UserProfiles ></UserProfiles>
</div>
);
}
export default App;
The image is not being loaded in the UI.
{
profile.userId ? (<img src={`http://localhost:5000/api/v1/users/${profile.userId}/image/download`} /> ) : <h5>No profile Image Uploaded</h5>
}
When I go to this http://localhost:5000/api/v1/users/565c6cbe-7833-482d-b0d4-2edcd7fc6163/image/download URL via browser.
It has a response with
ÿØÿàJFIFHHÿâICC_PROFILElcmsmntrRGB XYZ Ü)9acspAPPLöÖÓ-lcms descü^cprt\wtpthbkpt|rXYZgXYZ¤bXYZ¸rTRCÌ#gTRCÌ#b
Update Added Backend code.
controller
#GetMapping(path = "{userId}/image/download")
public byte[] downloadUserProfileImage(#PathVariable("userId") UUID userId) {
return userProfileService.downloadUserProfileImage(userId);
}
Service:
private UserProfile getUserProfileOrThrow(UUID userId) {
UserProfile userProfile = userProfileRepository.getUserProfiles()
.stream()
.filter(profile -> profile.getUserId().equals(userId)).findFirst().orElseThrow(() -> new IllegalStateException("User does not exist" + userId)
);
return userProfile;
}
public byte[] downloadUserProfileImage(UUID userId) {
UserProfile userProfile=getUserProfileOrThrow(userId);
String path = String.format("%s/%s",
BucketName.PROFILE_IMAGE.getBucketName(),
userProfile.getUserId());
return userProfile.getImageUrl()
.map(key -> fileStore.download(path, key))
.orElse(new byte[0]);
}
FileStore:
#Service
public class FileStore {
private final AmazonS3 s3;
#Autowired
public FileStore(AmazonS3 s3) {
this.s3 = s3;
}
public void save(String path,
String fileName,
Optional<Map<String, String>> optionalMetadata,
InputStream inputStream) {
ObjectMetadata metadata = new ObjectMetadata();
optionalMetadata.ifPresent(map -> {
if (!map.isEmpty()) {
map.forEach(metadata::addUserMetadata);
}
});
try {
s3.putObject(path, fileName, inputStream, metadata);
} catch (AmazonServiceException e) {
throw new IllegalStateException("Failed to store file to s3", e);
}
}
public byte[] download(String path, String key) {
try {
S3Object object = s3.getObject(path, key);
return IOUtils.toByteArray(object.getObjectContent());
} catch (AmazonServiceException | IOException e) {
throw new IllegalStateException("Failed to download file to s3", e);
}
}
}
Amazon s3 config:
#Configuration
public class AmazonConfig {
#Bean
public AmazonS3 s3(){
AWSCredentials awsCredentials=new BasicAWSCredentials("my-credentials","my-secret-key");
return AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(Regions.AP_SOUTH_1)
.build();
}
}
UserProfile:
public class UserProfile {
private final UUID userId;
private final String userName;
private String imageUrl;
//This might be null
public Optional<String> getImageUrl() {
return Optional.ofNullable(imageUrl);
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
//getters & setters
}

When I was facing the same issue, I had to return the object.getObjectContent() image in a Base64 format.
Afterwards, when displaying the data in the front-end, you can try and to this:
<img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
You can try this Base64 decoder to see if your Base64 data is correct or not.
That implies that you make the GET call beforehand, save the result and then display the base64 string in the img src
UPDATE:
Depending on your approach, in order to download the images for each user profile, inside the .map, you can have a function that downloads the picture for each profile.
const fetchUserProfileImage = async (userProfileId) => {
return axios.get(`http://localhost:5000/api/v1/users/${profile.userId}/image/download`)
}
return userProfiles.map(async (profile,index) => {
const userProfileImageBase64 = await fetchUserProfileImage(profile.userId)
return (
<div key={index}>
<MyDropZone userId={profile.userId}></MyDropZone>
<h3>{profile.userId}</h3>
{
profile.userId ? (<img src={`data:image/png;base64, ${userProfileImageBase64}`}/> ) : <h5>No profile Image Uploaded</h5>
}
</div>
);
})
Or if you don't like to wait inside the .map, you can try to download all of the images before rendering the body and mapping them to the already existing user in the userProfiles state.
Or, the best approach imo is to add another profileImageSrc field to the User class and save it there whenever you upload an image in the backend. Then you don't have to make extra calls and just consume the data received when fetching the userProfiles

The simplest trick is to transform your byte arrays into an actual image
#GetMapping(path ="/download/{fileName}")
public ResponseEntity<ByteArrayResource> downloadFile(#PathVariable String fileName) {
byte[] data = service.downloadImage(fileName);
ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity
.ok()
.contentLength(data.length)
.header("Content-type", "image/jpeg")
.header("Content-disposition", "attachment; filename=\"" + fileName + "\"")
.body(resource);
}
This will return an actual image whether the image was jpg or png it will not matter. You can actually view the image in postman and your frontend can pick it up directly.

Related

Unable to connect to Room: Invalid Access Token issuer/subject - Twilio visio room issues

I'm using twilio, Symfony 5 with the help of this tuto :
https://www.twilio.com/blog/create-group-video-chat-app-symfony-php-twilio-react
to create a video chat.
Upon entering the name of the room and launching the visio, I got the following error in the browser's console :
Unable to connect to Room: Invalid Access Token issuer/subject
I understand it's caused because the token generated by the route access_token must not be right.
I've created a normal twilio account then generated the API key, kept account sid, api key sid and api secret on my .env file.
Just in case I recreated a new api key to see if it changed something but didn't.
I've checked the doc for my error (https://www.twilio.com/docs/api/errors/20103) , but the solutions did not solve my problem.
Chat.js file:
import React, { useState } from "react";
import axios from "axios";
import Video from "twilio-video";
const Chat = () => {
const [roomName, setRoomName] = useState('');
const [hasJoinedRoom, setHasJoinedRoom] = useState(false);
const joinChat = event => {
event.preventDefault();
if (roomName) {
axios.post('/access_token', { roomName }, ).then((response) => {
console.log('RESPONSE: ');
console.log(response.data.token);
connectToRoom(response.data.token);
setHasJoinedRoom(true);
setRoomName('');
}).catch((error) => {
console.log(error);
})
} else {
alert("You need to enter a room name")
}
};
const connectToRoom = (token) => {
const { connect, createLocalVideoTrack } = Video;
let connectOption = { name: roomName };
connect(token, connectOption).then(room => {
console.log(`Successfully joined a Room: ${room}`);
const videoChatWindow = document.getElementById('video-chat-window');
createLocalVideoTrack().then(track => {
videoChatWindow.appendChild(track.attach());
});
room.on('participantConnected', participant => {
console.log(`Participant "${participant.identity}" connected`);
participant.tracks.forEach(publication => {
if (publication.isSubscribed) {
const track = publication.track;
videoChatWindow.appendChild(track.attach());
}
});
participant.on('trackSubscribed', track => {
videoChatWindow.appendChild(track.attach());
});
});
}, error => {
console.error(`Unable to connect to Room: ${error.message}`);
});
};
return(
<div className="container">
<div className={"col-md-12"}>
<h1 className="text-title">Symfony React Video Chat</h1>
</div>
<div className="col-md-6">
<div className={"mb-5 mt-5"}>
{!hasJoinedRoom && (
<form className="form-inline" onSubmit={joinChat}>
<input type="text" name={'roomName'} className={"form-control"} id="roomName"
placeholder="Enter a room name" value={roomName} onChange={event => setRoomName(event.target.value)}/>
<button type="submit" className="btn btn-primary">Join Room</button>
</form>
)}
</div>
<div id="video-chat-window"/>
</div>
</div>
)
};
export default Chat;
TokenController file to generate user's token :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Twilio\Jwt\AccessToken;
use Twilio\Jwt\Grants\VideoGrant;
class TokenController extends AbstractController
{
/**
* #Route("/token", name="token")
*/
public function index()
{
return $this->render('token/index.html.twig', [
'controller_name' => 'TokenController',
]);
}
/**
* #param Request $req
* #return \Symfony\Component\HttpFoundation\JsonResponse
* #Route("access_token", name="access_token", methods={"POST"})
*/
public function generateToken(Request $req) {
$accountSid = getenv('ACCOUNT_SID');
$apiKeySid = getenv('API_SID');
$apiKeySecretSid = getenv('API_SECRET');
$identity = uniqid();
$roomName = json_decode($req->getContent());
$token = new AccessToken(
$accountSid,
$apiKeySid,
$apiKeySecretSid,
3600,
$identity
);
$grant = new VideoGrant();
$grant->setRoom($roomName->roomName);
$token->addGrant($grant);
return $this->json(['token' => $token->toJWT()], 200);
}
}
And this line in the **app.js** :
ReactDOM.render(<Chat/>, document.getElementById("root"));
With the proper imports.
UPDATE
Solution found! Thanks for the help !
getenv() method was the issue.
I now inject my .env info in my controller as parameters as follow :
services.yaml
parameters:
app.env.ACCOUNT_SID: '%env(resolve:ACCOUNT_SID)%'
app.env.API_KEY: '%env(resolve:API_KEY)%'
app.env.API_SECRET: '%env(resolve:API_SECRET)%'
And recover them in the controller
Controller to recover the data
public function somename() {
$sid = $this->getParameter('app.env.ACCOUNT_SID');
$apiKey = $this->getParameter('app.env.API_KEY');
$apiSecret = $this->getParameter('app.env.API_SECRET');
$identity = uniqid();
$token = new AccessToken(
$sid,
$apiKey,
$apiSecret,
3600,
$identity
);
}
And works like a charm
may be it could help those who has this issue.
I have found that the default region used in the exemple code was "US1" and that I have create my API KEY in the region "IE1".
Creating and using an API KEY made in the US1 region has resolve my problem and has made the exemple code work.
Now because I am in Europe region, i will try to figure how to set the default region as "IE1" in the SDK.

Google Apps Script & React.js : DELETE https://mygoogleappapi.com/exec 405 (Method Not Allowed)

Thank you for reading!
I am learning how to use GAS now,
I can't delete the specific row I selected on google spread sheet.
I got the theme error after trying to delete using "axios.delete method" when I used react app and google script api.
I already passed GET method and POST method using axios. Actually , I could get and post my data from my google spread sheet.
but deleting could not access well.
I found this error 405 is not allowed to access my google sheet, but Why can I get this error even though the post method was accessible?
My App script or My react.js code need to have any code else ? I can't solve this problem...
I want to solve this error and delete the specific row I selected. Also, I want to know a workaround for this error.
Do you have any idea ? If you have some good idea,Could you tell me please ?
Thank you for reading.
this is my App script code.
function doDelete(req, sheet) {
var id = req.parameter.id;
var Row = sheet.getLastRow();
for (var i = 1; i <= Row; i++) {
var idTemp = sheet.getRange(i, 1).getValue();
if (idTemp == id) {
sheet.deleteRow(i);
}
}
}
this is my reactjs code.
import React,{ useState , Component } from 'react';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
var optionAxios = {
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Access-Control-Allow-Origin':'*' ,
}
}
const api = 'https://mygoogleappscriptapi.com/exec';
class Price extends Component {
constructor(){
super();
this.state = {
info: []
};
this.getInfo();
this.createInfo = this.createInfo.bind(this);
this.deleteInfo = this.deleteInfo.bind(this);
};
// accessed get!
getInfo = () =>{
axios.get(api)
.then((res) =>{
console.log(res.data)
this.setState({
info: res.data
})
})
}
// accessed post!
createInfo = () =>{
axios.post(api,{
product: "hoge",
price: 1000,
miniLot: 1000,
cartonSize: "40*30*50"
},optionAxios)
.then((res) => {
this.getInfo(res);
})
}
// cant't access delete!
deleteInfo = (e) => {
console.log(e);
axios.delete(api,{
id: e,
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
render(){
return (
<div className={this.root}>
<Grid container>
<Grid item xs={11}>
<button onClick={this.createInfo}>createButon</button>
<Paper>
{this.state.info.map(info => <div key={info.id}>
{info.product}
<button onClick={() => this.deleteInfo(info.id)}>×</button>
</div>)}
</Paper>
</Grid>
</Grid>
</div>
);
}
}
export default Price;
Only the following HTTP methods are supported:
POST
GET
DELETE method is not supported by google-apps-script-web-application.
You can use post:
Server side:
function doPost(e){
if(e.parameter.option === "DELETE") return doDelete(e);
/*rest of doPost here*/
}
React:
// convert to axios.post
deleteInfo = (e) => {
console.log(e);
axios.post(api,{//modified
id: e,
option: "DELETE",//added
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
Try this:
function doDelete(req, sh) {
var id = req.parameter.id;
const ss=SpreadsheetApp.getActive();
sh=sh||ss.getActiveSheet();
var vs=sh.getRange(1,1,sh.getLastRow(),1).getValues();
var d=0;
for (var i=0;i<vs.length;i++) {
if (vs[i][0]== id) {
sh.deleteRow(i+1-d++);
}
}
}

Observable to Array *ngFor saying undefined

I am new to Angular. I have a Node and Express backend pulling data from an MS SQL database. If I go to the endpoint URL it displays my data as JSON. I am running on localhost so I set a proxy for CORS. I have a class that defines the data, a service that pulls the data from the endpoint and a component that tries to set an array equal to the data pulled from the service. The HTML has an *ngFor that is supposed to loop through the values and display them in a grid.
If I call my data in my component through my service, so this.userService.getUsers(), and do a console.log I can see the recordset in the browser console. I try to set the array equal to the userService.getUsers() and then call the array and I get "undefined". Being that I am new, I have tried to follow the Heroes tutorial and that did not work. I spent a day searching Google and trying different solutions that I have come across but they all come up as undefined. I will attach the code here. If someone can guide me a bit, it would be much appreciated.
User class defining User:
export class User{
id: number;
ccn: string;
firstName: string;
lastName: string;
email: string;
}
User Service doing Http request:
import { Injectable } from '#angular/core';
import { User } from './user';
import { USERS } from './mock-users';
import { MessageService } from './message.service';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class UserService {
private userURL = 'api/users'
//private userURL = 'localhost:5000'
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
constructor(
private http: HttpClient,
private messageService: MessageService) { }
//getUsers(): Observable<User[]> {
// this.messageService.add('UserService: fetched users');
// return of(USERS);
//}
/** GET users from the server */
getUsers(): Observable<User[]> {
//console.log('getting users');
return this.http.get<User[]>("http://localhost:5000/api/user")
.pipe(
tap(_ => this.log('Fetched users')),
catchError(this.handleError<User[]>('getUsers', []))
);
//return this.http.get<User[]>("http://localhost:5000/api/user");
//console.log('got users');
}
/* GET heroes whose name contains search term */
searchUsers(term: string): Observable<User[]> {
if (!term.trim()) {
// if not search term, return empty hero array.
return of([]);
}
return this.http.get<User[]>(`${this.userURL}/?ccn=${term}`).pipe(
tap(_ => this.log(`found users matching "${term}"`)),
catchError(this.handleError<User[]>('searchUsers', []))
);
}
addUser (user: User): Observable<User> {
return this.http.post<User>(this.userURL, user, this.httpOptions).pipe(
tap((newUser: User) => this.log(`added user w/ id=${newUser.id}`)),
catchError(this.handleError<User>('addUser'))
);
}
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error);
this.log(`${operation} failed: ${error.message}`);
return of(result as T);
};
}
private log(message: string) {
this.messageService.add(`User service: ${message}`);
}
}
Display Users Component TS file:
import { Component, OnInit } from '#angular/core';
//import { USERS } from '../mock-users';
import { UserService } from '../user.service';
import { User } from '../user';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { element } from 'protractor';
#Component({
selector: 'app-display-users',
templateUrl: './display-users.component.html',
styleUrls: ['./display-users.component.css']
})
export class DisplayUsersComponent implements OnInit {
users: User[] = [];
constructor(private userService: UserService) { }
//users$ = this.getUsers();
ngOnInit() {
this.getUsers();
console.log(this.userService.getUsers());
this.userService.getUsers().forEach(element => {
console.log(element);
});
}
getUsers(): void {
/*this.userService.getUsers()
.subscribe(users => this.users = users);*/
const userObservable = this.userService.getUsers();
userObservable.subscribe((userData: User[]) => {
this.users = userData;
});
}
}
Display Users Component HTML:
<div class="clr-row">
<div class="clr-col-lg-11 clr-col-md-11 clr-col-11 main-div">
<div class="card card-style" style="box-shadow: 0 0 0 0;">
<div class="card-header">
<h1><img src="../assets/images/BSOLOGO_gray.png" class="title-img"><span class="title"> Users</span></h1>
</div>
<div class="card-block">
<div class="card-title">
<clr-datagrid>
<clr-dg-column>CCN</clr-dg-column>
<clr-dg-column>Last Name</clr-dg-column>
<clr-dg-column>First Name</clr-dg-column>
<clr-dg-column>Email</clr-dg-column>
<clr-dg-row *ngFor="let user of users">
<clr-dg-cell>{{user.ccn}}</clr-dg-cell>
<clr-dg-cell>{{user.lastName}}</clr-dg-cell>
<clr-dg-cell>{{user.firstName}}</clr-dg-cell>
<clr-dg-cell>{{user.email}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>{{users.length}} users</clr-dg-footer>
</clr-datagrid>
</div>
</div>
</div>
</div>
</div>
Any help would be greatly appreciated!
UPDATED
Ypu can replace getUsers on both classes by these. HTML looks fine to me. I converted users to public too.
//userService
getUsers(callback: Function) {
return this.http.get<User[]>("http://localhost:5000/api/user")
.subscribe(
response => callback(response)
);
}
//Component
public users: User[] = [];
getUsers(): void {
this.userService.getUsers((result) => {this.users = result;})
}
If you do not need it to be Observable you can use toPromise() and using async/await makes it waaay easier
Service
async getUsers(): Promise<User[]> {
return await this.http.get<User[]>('http://localhost:5000/api/user').toPromise();
}
Component.ts
users: User[] = [];
async ngOnInit() {
this.users = await this.userService.getUsers();
}
Component.html
<clr-datagrid *ngIf="users">
<clr-dg-column>CCN</clr-dg-column>
<clr-dg-column>Last Name</clr-dg-column>
<clr-dg-column>First Name</clr-dg-column>
<clr-dg-column>Email</clr-dg-column>
<clr-dg-row *ngFor="let user of users">
<clr-dg-cell>{{user.ccn}}</clr-dg-cell>
<clr-dg-cell>{{user.lastName}}</clr-dg-cell>
<clr-dg-cell>{{user.firstName}}</clr-dg-cell>
<clr-dg-cell>{{user.email}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>{{users.length}} users</clr-dg-footer>
</clr-datagrid>
My issue has been resolved. In my SQL statement I was calling SELECT * FROM table FOR JSON PATH which was creating a weird object being pulled from the server. Removing the FOR JSON PATH provided JSON data. Then the second part of my issue was mapping my DB fields with my user class.
This was done like this:
request.query('SELECT * FROM Table ORDER BY myField', function (err, recordset) {
if (err) console.log(err);
const records = recordset.recordset;
const result = records.map(r => { return { id: r.tableID, field1: r.dbField1, field2: r.dbField2, field3: r.dbField3, field4: r.dbField4}});
res.send(result);
});
I hope this helps someone! Thanks to everyone that posted to help me.

NativeScript-vue Save Image from url

I will like to save an image from a web URL to the device can anyone help?
I've tried using the imageSource module but it only speaks of images from local device
const imageSourceModule = require("tns-core-modules/image-source");
const fileSystemModule = require("tns-core-modules/file-system");
imageSourceModule.fromUrl(webURL).then((res) => {
const folderDest = fileSystemModule.knownFolders.currentApp();
const pathDest = fileSystemModule.path.join(folderDest.path, "test.png");
const saved = res.saveToFile(pathDest, "png");
if (saved) {
console.log("Image saved successfully!");
this.image = imageSourceModule.fromFile(pathDest);
}
thanks to #Narendra Mongiya for the first answer which help get the image from url
This is what your code should be (I am assuming that your webURL returns jpg/png etc)
const imageSource = require('image-source');
imageSource.fromUrl(webURL).then((res: any) => {
this.imageUrl = res;
}).catch(err => {
this.imageUrl = this.getIconSource('default_image');
});
and in html file
<Image [src]="imageUrl" loadMode="async"></Image>
and for the default images if URL return blank
public getIconSource(icon: string): string {
return isAndroid ? `res://${icon}` : 'res://images/' + icon;
}

MERN+ Cloudinary: Unsupported source URL

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.

Resources