antd upload api: how to return promise - javascript

I am working with antd framework and I have to use upload API.
This is the signature:
action: Uploading URL : string|(file) => Promise
I am invoking the API in this way trying to return a Promise:
<Upload {...propsUpload}>
<Button> <Icon type="upload" />Upload</Button>
</Upload>
with propsUpload that refers to function uploadMedia
const propsUpload = {
action: this.uploadMedia,
listType: 'picture',
defaultFileList: [],
className: 'upload-list-inline',
};
and this is the function uploadMedia
uploadMedia = (file) => {
let formData = new FormData();
formData.append('file', file);
formData.append('uuid', this.generateuuid());
formData.append('domain', 'POST');
formData.append('filename', file.name );
return fetch(process.env.REACT_APP_API_URL +
'/v100/media/upload', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json'
},
body: formData
})
.then(response => response.json())
.then(data => data.data)
.catch(error => {
console.log('Error fetching profile ' + error)
})
}
The file is uploaded to server correctly.
But after the call to API, antd try to do another call that fails, maybe because I am not returning the correct value from function.
As result the thumbnail is displayed with red border and and error is shownd. In the image below there are both (the call that fails and image with red border)
What type of object I have to return in function uploadMedia to use api correctly?
Thanks

I haven't used antd but looking at the docs of Uplaod component I think you're using it wrong. Look at the examples there and see the code, action expects either a URL or a Promise that will return this URl. And Upload in this case will make request itself, so you don't need to do fetch. And your promise returns the data (object) so the Upload sends the request to [object Object] (which is what's returned by .toString() when applied to an object in JS)
EDIT
Try to check all examples in docs, I can see that there is an example when you want to manually upload the file (if you really need it)

For anyone looking to access the response object after calling the API. There are two ways you can get access to the response.
Implement a custom API request mentioned as in other answers for this question.
Use the onChange method provided by AntD (Which is the easier than utilizing the custom request)
I will explain the second approach below using a code block.
const fileUploadProps = {
name: "file",
action: config.remote + "api/file",
method: "POST",
showUploadList: false,
headers: {
authorization: "authorization-text",
contentType: "multipart/form-data"
},
onChange(info) {
if (info.file.status === "done") {
const { response } = info.file;
updateProfile(response.payload.file);
} else if (info.file.status === "error") {
message.error("Error uploading the file");
props.endLoad();
}
},
beforeUpload(file) {
const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
if (!isJpgOrPng) {
message.error("You can only upload JPG/PNG file!");
}
const isLt2M = file.size / 1024 / 1024 < 2;
const isGT20K = file.size / 1024 > 20;
if (!isLt2M) {
message.error("Image must smaller than 2MB!");
}
if (!isGT20K) {
message.error("Image must larger than 20KB!");
}
if (isJpgOrPng && isLt2M && isGT20K) {
props.startLoad();
return true;
} else {
return false;
}
}
};
In Render function I have the AntD upload component
<Upload {...fileUploadProps}>
<Button icon={<CameraFilled style={{ fontSize: "30px" }} />}></Button>
</Upload>
You can notice how I got the access to the response object inside onChange function.
Once the upload is complete it will call the onChange function having response object inside the info object.
So from there you can get access to your data object easily and call the next method.

I solved using api customRequest in this way:
uploadMedia = (componentsData) => {
let formData = new FormData();
formData.append('file', componentsData.file);
formData.append('uuid', this.generateuuid());
formData.append('domain', 'POST');
formData.append('filename', componentsData.file.name );
fetch(process.env.REACT_APP_API_URL + '/v100/media/upload', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json'
},
body: formData
})
.then(response => response.json())
.then(data => data.data)
.then(data=> componentsData.onSuccess())
.catch(error => {
console.log('Error fetching profile ' + error)
componentsData.onError("Error uploading image")
})
}

For those who are not clear how to actually implement it (and it is unclear in docs):
Just implement a customRequest function in the props that accepts two callbacks, which are onError and onSuccess, and other data such as file and filename.
Like this
const props = {
customRequest: (componentsData) => {
let formData = new FormData();
formData.append('file', componentsData.file);
formData.append('uuid', this.generateuuid());
formData.append('domain', 'POST');
formData.append('filename', componentsData.file.name );
fetch(process.env.REACT_APP_API_URL + '/v100/media/upload', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json'
},
body: formData
})
.then(response => response.json())
.then(data => data.data)
.then(data=> componentsData.onSuccess())
.catch(error => {
console.log('Error fetching profile ' + error)
componentsData.onError("Error uploading image")
})
}
}
And let Upload component receive the props.
const App = () => {
return <Upload {...props} />
}

Related

How can I POST an image to DB via react native with the fetch API?

So I am trying to POST an image to a server via React Native and the fetch API.
fetch(`${API}/uploadAvatar`, {
method: "POST",
headers: {
Authorization: bearer,
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "application/json",
},
body: JSON.stringify({ file: result.uri }),
})
.then((response) => response.json())
.then((json) => {
console.log({ json });
// this console.log outputs:
// "The format of the file should be jpg, png, jpeg.",
})
.catch((err) => {
console.log({ err });
});
}
result returns this:
{
"cancelled": false,
"height": 1776,
"type": "image",
"uri": "file:///var/mobile/Containers/Data/Application/18F84F29-CB72-4615-A68F-A00422D9B119/Library/Caches/ExponentExperienceData/%2540heythere%252Fkeep-up/ImagePicker/959E8BDE-FCF4-40C6-AF18-8F9EA852760D.jpg",
"width": 1776,
}
Those are the calls on POSTMAN where you can see they work.
What am I doing wrong?
Your postman shows that you're using form-data to upload the image, but in your code you're simply making a JSON post call without sending any form-data. You need to create a new FormData instance, and append data to it. In your case, you want to send the result.uri with the key file, this can be done using formData.append('file', result.uri). Then you gotta send the formData instance as your body (with method as POST, in your case)
let formData = new FormData();
formData.append('file', result.uri);
fetch("api/SampleData", {
body: formData,
method: "post"
}).then((response) => response.json())
.then((json) => {
console.log({
json
});
})
.catch((err) => {
console.log({
err
});
});
You can post images to the server with the help of Form Data by creating a JSON object of the file path, file name, and file type and append the object into the Form Data instance with the parameter. The path of the file is Platform-specific therefore you have to add conditions for the path. Please refer to the code snippet.
let Data = new FormData();
Data.append('file',
{
uri: Platform.OS === 'android' ? result.uri: result.uri.replace('file://',''),
type: result.type,
name: result.uri.replace(/^.*[\\\/]/, '')
});
fetch("api/SampleData", {
body: Data,
method: "post",
headers: {'Content-Type': 'multipart/form-data'}
}).then((response) => response.json())
.then((json) => {
console.log({
json
});
})
.catch((err) => {
console.log({
err
});
});

Problems passing parameters through the put method using fetch

I'm having problems trying to pass parameters by the put method using fetch
For this I am trying the following
fetch(`brands/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({name: 'dummy text'})
})
.then(response => response.json())
.then(json => {
if (json.status === 'ok') {
// do some relevant logic
return false;
}
showErrors(json.errors);
})
.catch(error => console.error(error.message));
I am also trying using the FormData interface
const formData = new FormData();
formData.append('name', 'some dummy text');
fetch(`brands/${id}`, {
method: 'PUT',
body: formData
})
.then(response => response.json())
.then(json => {
if (json.status === 'ok') {
// Some relevant logic
return false;
}
showErrors(json.errors);
})
.catch(error => console.error(error.message));
But I am getting the same result (name parameter is not in the controller)
Inspected the network tab I can see that ajax has been called and in the params tab I can see that the variables are passed. But when trying to access these parameters from the controller they do not appear.
I appreciate your help
In backend when printing the parameters received in this query, the name parameter is not listed.
In backend the relevant parts in the controller definition are the following
The update method can only be invoked through the put method
static allowedMethods = [
save: 'POST',
update: 'PUT'
]
Here I hope that the name parameter has a value, but the parameter does not exist
def update() {
try {
Brand brand = brandService.update(params.id, params.name)
render(contentType: 'application/json') {
[status: 'ok', brand: brand]
}
} catch(ValidationException e) {
render(contentType: 'application/json') {
[status: 'fail', errors: e.errors]
}
}
}
Running your form through this little helper might do the trick:
form2Obj(form) {
const obj = {};
new FormData(form).forEach((val, key) => obj[key] = val);
return obj;
}

Missing request token for request

Get all contacts from phonebook and upload to server but got following error.
While append image in request body FormData
Tried code
pass file url contact thumbnailPath
const path = con.thumbnailPath
body.append('image', {
uri: path,
type: 'image/jpeg',
name: 'photo.jpg',
type: 'multipart/form-data'
})
Tried code
pass file url contact thumbnailPath without "file://"
const path = con.thumbnailPath.replace('file://', '')
body.append('image', {
uri: path,
type: 'image/jpeg',
name: 'photo.jpg',
type: 'multipart/form-data'
})
Tried code
check file exist on path or not with using react-native-fs
if (con.thumbnailPath != '') {
let isExist = RNFS.exists(con.thumbnailPath)
if (isExist) {
const path = con.thumbnailPath.replace('file://', '')
console.log("Exist", path)
body.append('image', {
uri: path,
type: 'image/jpeg',
name: 'photo.jpg',
type: 'multipart/form-data'
})
}
}
Request
fetch(url, {
method: 'POST',
headers: {
'Authorization': token,
'token': token
},
body: params
})
.then((res) => res.json())
.then((json) => {
console.log("RESPONSE:- ", json)
if (json.response[0].status == 'false') {
let msg = json.response[0].response_msg
callback(new Error(msg), json.response[0])
}
else {
callback(null, json.response[0])
}
})
.catch((err) => {
console.log(err)
callback(err, null)
})
The issues comes from react-native#0.63.2's internal bug.
A quick solution is to revert this commit: https://github.com/facebook/react-native/commit/31980094107ed37f8de70972dbcc319cc9a26339#diff-9a034658197479288c4d346a0eb4d98c
After manually revert this commit in node_modules, recompile the app and the image uploading will be working without any issues.
Replace the function loadImageForURL in /Libraries/Image/RCTLocalAssetImageLoader.mm with the following:
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
__block auto cancelled = std::make_shared<std::atomic<bool>>(false);
RCTExecuteOnMainQueue(^{
if (cancelled->load()) {
return;
}
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
if (image) {
if (progressHandler) {
progressHandler(1, 1);
}
completionHandler(nil, image);
} else {
NSString *message = [NSString stringWithFormat:#"Could not find image %#", imageURL];
RCTLogWarn(#"%#", message);
completionHandler(RCTErrorWithMessage(message), nil);
}
});
return ^{
cancelled->store(true);
};
}
This problem is fixed in 0.63.3 ✅
**For IOS** in
node_modules/react-native/Libraries/Image/RCTLocalAssetImageLoader.mm file
**Replace Below**
- -(RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
if (image) {
if (progressHandler) {
progressHandler(1, 1);
}
completionHandler(nil, image);
} else {
NSString *message = [NSString stringWithFormat:#"Could not find image %#", imageURL];
RCTLogWarn(#"%#", message);
completionHandler(RCTErrorWithMessage(message), nil);
}
return nil;
}
**With**
- -(RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
__block auto cancelled = std::make_shared<std::atomic<bool>>(false);
RCTExecuteOnMainQueue(^{
if (cancelled->load()) {
return;
}
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
if (image) {
if (progressHandler) {
progressHandler(1, 1);
}
completionHandler(nil, image);
} else {
NSString *message = [NSString stringWithFormat:#"Could not find image %#", imageURL];
RCTLogWarn(#"%#", message);
completionHandler(RCTErrorWithMessage(message), nil);
}
});
return ^{
cancelled->store(true);
};
}
This..
Like and Love , if it work
I have the same issue which perfectly reproducible on one of the iPhone 7 on my react-native project. It's strange but another iPhone 7's works perfectly as well as all Android devices.
My code:
formdata.append("file", {uri: photo.uri, name: name_img, type: 'image/jpeg' });
axios({
url: `${API}${'/upload'}`,
method: 'post',
headers: {
'Authorization': 'Basic ' + auth_token,
'Content-Type':'application/x-www-form-urlencoded'
},
data: formdata
}).then(response => this.saveRoute())
.catch(err => {
this.props.errorMessage({message: err})
}
})
Few things that I investigate:
I was not able to catch it in debug mode (seams smth wrong in async calls?)
I was not able to catch it with try-catch statement but seams it happened in Axios call.
So, I tried to play with Timeout and was able to make it totally unreproducible with 300ms timeout before Axios call.
formdata.append("file", {uri: photo.uri, name: name_img, type: 'image/jpeg' });
setTimeout(() =>
axios({
url: `${API}${'/upload'}`,
method: 'post',
headers: {
'Authorization': 'Basic ' + auth_token,
'Content-Type':'application/x-www-form-urlencoded'
},
data: formdata
}).then(response => this.saveRoute())
.catch(err => {
this.props.errorMessage({message: err})
}
})
, 300);
I know that it's a workaround but may help others to understand the issue for more deep research.
I temporary fixed using rn-fetch-blob, but the issue is present in 0.63.2 version and I didn't want to patch node_modules react-native images library.
To send file, you have to create a FormData and append your file into it. See EX: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Uploading_a_file
I found solution. Just put delay when you post request.
let options = {}
options.headers = headers
options.method = 'POST'
let url = {SERVER_URL}
options.body = new FormData();
for (let key in data) {
options.body.append(key, data[key]);
}
setTimeout(() => {
fetch(url, options)
.then((response) => response.json())
.then((responseJson) => {
resolve(responseJson);
})
.catch((error) => {
let errParam = {}
errParam.errMsg = error.toString()
console.log(errParam)
resolve(errParam);
})
}, 1000);
Error fixed when updating React Native to version 0.63.3

React Native Fetch give error "Network request failed"

I have the following code for creating an event with a image and some body params. It was working fine when i was doing it without image, i am using react-native-image-crop-picker for selecting images. I am getting "Network request failed" error when posting data from react-native. The request never reach my backend as i am getting no logs there. It is working fine with postmen.
MY CODE:
const { name, date, description, location, uri, mime, time } = this.state;
const formData = new FormData();
formData.append('name', name)
formData.append('date', date)
formData.append('description', description)
formData.append('location', location)
formData.append('time', time)
formData.append('image',{
uri:uri,
mime:'image/jpeg',
name:`image${moment()}`
})
alert(JSON.stringify(formData));
const config = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data',
},
body: formData,
};
fetch(`http://${Config.apihost}:${Config.port}/events`,config).then((res) => res.json())
.then((res) => {
this.setState({ modalVisible: false, name:'', date: moment().format('YYYY-MM-DD'), description:'', Location: 'AlHedaya Masjid' })
this.props.addEvent(res.message);
// this.props.navigation.goBack();
}).catch((err) => alert(err));
I have another screen which contains different number of pictures like gallery i am uploading multiple picture to the gallery, the request is working fine with code below.
const data = new FormData();
data.append('name', 'avatar');
images.map((res, i) => {
data.append('fileData[]', {
uri: res.path,
type: res.mime,
name: `image${i}${moment()}`
});
})
const config = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data',
},
body: data,
};
fetch(`http://${Config.apihost}:${Config.port}/events/${this.state.item.id}/photos`, config)
.then((checkStatusAndGetJSONResponse) => checkStatusAndGetJSONResponse.json())
.then((response) => {
if (response.status && response.message.length > 0) {
var images = this.state.images;
response.message.map(file => {
images.push(`http:${Config.apihost}:${Config.port}/images/${file.id}`);
});
this.setState({ images });
}
}).catch((err) => { alert(err) });
I can't really see the difference between the two codes but the upper code giving me error.
I am testing on android
I am using the IP address instead of localhost (my others requests are working so thats out of equation)
None of the solution in this link worked
React Native fetch() Network Request Failed
Am I missing something?
In first code snippet you have written mime instead of type.
formData.append('image',{
uri:uri,
**mime:'image/jpeg**',
name:`image${moment()}`
})
it should be like below snippet
formData.append('image',{
uri:uri,
type:'image/jpeg',
name:`image${moment()}`
})

Upload video in react-native

Is there a way to upload video to a server in react native?
I've looked into the react-native-uploader plugin on github, but it doesn't give any guidance on video uploads if it's even possible with that plugin.
Just use fetch for uploading
let formData = new FormData();
formData.append("videoFile", {
name: name.mp4,
uri: video.uri,
type: 'video/mp4'
});
formData.append("id", "1234567");
try {
let response = await fetch(url, {
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
body: formData
});
return await response.json();
}
catch (error) {
console.log('error : ' + error);
return error;
}
Here is another answer, using rn-fetch-blob in RN 0.57.8.
postVideo = (video,url) => {
RNFetchBlob.fetch('POST',url, {
'content-type': 'multipart/form-data',
"Accept":"multipart/form-data",
'access-token': AuthToken.token, //token from server
},[
//the value of name depends on the key from server
{name: 'video', filename: 'vid.mp4', data: RNFetchBlob.wrap(video.uri) },
]).then(response => response.json())
.then(response => {
if (response.status === 'success') {
alert("Upload success");
this.props.navigation.navigate('publish');
} else {
alert(response.msg);
}})
.catch((err) => {
alert(err);
})
}
Yes, it is possible.
you have to be running React Native 0.45.0 or higher, which do support accessing videos from camera roll.
you have to receive reference to image/video access from camera roll by calling CameraRoll.getPhotos(params) (more on this in docs)
then, use RNUploader.upload(...) to send assets to your serve (you need to link lib before!)

Categories

Resources