I'm using fetch for requests, and I want to submit a file along with other data. How can I do so?
If it is not possible using fetch, what should I do then?
Thanks you guys.
react-native-fs
Has support for file upload on iOS
From the README:
// require the module
var RNFS = require('react-native-fs');
var uploadUrl = 'http://requestb.in/XXXXXXX'; // For testing purposes, go to http://requestb.in/ and create your own link
// create an array of objects of the files you want to upload
var files = [
{
name: 'test1',
filename: 'test1.w4a',
filepath: RNFS.DocumentDirectoryPath + '/test1.w4a',
filetype: 'audio/x-m4a'
}, {
name: 'test2',
filename: 'test2.w4a',
filepath: RNFS.DocumentDirectoryPath + '/test2.w4a',
filetype: 'audio/x-m4a'
}
];
var uploadBegin = (response) => {
var jobId = response.jobId;
console.log('UPLOAD HAS BEGUN! JobId: ' + jobId);
};
var uploadProgress = (response) => {
var percentage = Math.floor((response.totalBytesSent/response.totalBytesExpectedToSend) * 100);
console.log('UPLOAD IS ' + percentage + '% DONE!');
};
// upload files
RNFS.uploadFiles({
toUrl: uploadUrl,
files: files,
method: 'POST',
headers: {
'Accept': 'application/json',
},
fields: {
'hello': 'world',
},
begin: uploadBegin,
progress: uploadProgress
})
.then((response) => {
if (response.statusCode == 200) {
console.log('FILES UPLOADED!'); // response.statusCode, response.headers, response.body
} else {
console.log('SERVER ERROR');
}
})
.catch((err) => {
if(err.description === "cancelled") {
// cancelled by user
}
console.log(err);
});
Related
I figured out it is not possible to share video content onto Instagram directly on Android in behalf of the user. This was my original intention.
Seems I can not use navigator.share call which works on iOS but not on Android, or only for text and url, but not for video / mp4.
I would not use MediaStore and similar React Nativ solutions as my app is a regular React.
What elso approach do you know?
I have now this:
const showShareDialog = async () => {
try {
log(
{
text: 'setIsDownloadFileInProgress(true) ' + getTempUserShortId(),
},
env
)
setIsDownloadFileInProgress(true)
const res = await axios.get(
`https://t44-post-cover.s3.eu-central-1.amazonaws.com/${imgId}.mp4`,
{ withCredentials: false, responseType: 'blob' }
)
setIsDownloadFileInProgress(false)
const shareData = {
files: [
new File([res.data], `${imgId}.mp4`, {
type: 'video/mp4',
}),
],
title: 'Megosztom',
text: 'Mentsd le a képekbe, és oszd meg storyban.',
}
//showShareDialog(shareData, 0)
if (navigator.share && !isDesktop) {
log(
{
text:
`navigator.share && !isDesktop shareData.files[0].size: ${shareData.files[0].size} ` +
getTempUserShortId(),
},
env
)
if (isAndroid()) {
const { status } = await MediaLibrary.requestPermissionsAsync()
if (status === 'granted') {
const asset = await MediaLibrary.createAssetAsync(res.data)
await MediaLibrary.createAlbumAsync(`${imgId}.mp4`, asset, false)
}
} else {
navigator
.share(shareData)
.then(() => {
log(
{
text: `'The share was successful 1 ` + getTempUserShortId(),
},
env
)
})
.catch((error) => {
log(
{
text:
`There was an error sharing 1 ${error} ` +
getTempUserShortId(),
},
env
)
})
}
} else {
log(
{
text: 'else ' + getTempUserShortId(),
},
env
)
setIsDesktopDownloadDialogVisible(true)
}
} catch (error) {
console.error(error)
log(
{
text: `a onclick error 2: ${error} ` + getTempUserShortId(),
},
env
)
}
}
I am creating an app with cordova for android using plugin file-transfer. The download is going to a certain folder but I can't read the file despite indicating the correct path follows the code
var uri = encodeURI(mypage);
var fileURL = cordova.file.externalDataDirectory + "teste.ogg";
fileTransfer.download(
uri, fileURL, function(entry) {
console.log("download complete: " + entry.toURL());
$("#audio-teste").attr('src',fileURL)
/*--codigo de teste--*/
var meuFile = cordova.file.externalDataDirectory;
resolveLocalFileSystemURL(meuFile, function(entry) {
var readerN = fileSystem.createReader();
readerN.readEntries(
function (entry) {
var arrayN =[];
for(var i="0"; i < entry.length; ++i){
var entradaN = entry[i].name;
arrayN.push(entradaN);
//console.log(array);
console.log('teste aq' + arrayN);
}
}
)
//console.log(entry);
});
/*----*/
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("download error code" + error.code);
},
false, {
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
/*----*/
The cordova plugin cordova-plugin-file-transfer is due to be deprecated and its latest npm version fails on iOS.
Therefore these two working functions are purely based on vanilla JS, and thus no need to use extra plugins, besides the standard plugin cordova-plugin-file. Therefore this is compatible with any platform.
https://gist.github.com/jfoclpf/07e52f6bdf9c967449c4bc06af44c94a
I paste here for your convenience:
// for different types of cordovaFileSystem check here:
// https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/#where-to-store-files
// or simply type in the console `console.log(cordova.file)`
function downloadFileToDevice (fileurl, filename, cordovaFileSystem, callback) {
var onerror = (err) => {
console.error(`Error downloading from ${fileurl} to cordovaFileSystem ${cordovaFileSystem}`,
err, new Error(err))
if (typeof callback === 'function') { callback(Error(err)) }
}
var blob = null
var xhr = new XMLHttpRequest()
xhr.open('GET', fileurl)
xhr.responseType = 'blob' // force the HTTP response, response-type header to be blob
xhr.onload = () => {
blob = xhr.response // xhr.response is now a blob object
var DataBlob = blob
window.resolveLocalFileSystemURL(cordovaFileSystem, (dirEntry) => {
const sanitizedFilename = filename.replace(/[^a-z0-9\.]/gi, '_').toLowerCase() // sanitize filename
dirEntry.getFile(sanitizedFilename, { create: true }, (file) => {
file.createWriter((fileWriter) => {
fileWriter.write(DataBlob)
if (typeof callback === 'function') { callback(null, cordovaFileSystem + sanitizedFilename) }
}, (err) => { console.error('Error on file.createWriter'); onerror(err) })
}, (err) => { console.error('Error on dirEntry.getFile'); onerror(err) })
}, (err) => { console.error('Error on resolveLocalFileSystemURL'); onerror(err) })
}
xhr.onerror = (err) => { console.error('Error on XMLHttpRequest'); onerror(err) }
xhr.send()
}
An example for downloading a file
downloadFileToDevice('https://example.com/img.jpg',
'myImg.jpg',
cordova.file.cacheDirectory,
(err, localFilePath) => {
if (err) {
console.error('An error occured downloading file:', err)
} else {
console.log('Download file with success: ' + localFilePath)
}
})
Can you try with this? I tried and it works like a charm
I am developing a React native app. I am uploading a batch of images using react-native-image-crop-picker. Using the way that storing the path in an array and one by one uploading the image. but I can't produce the downloadable link for the uploaded files. I am getting 2 errors
1)Can not read property 'then' of undefined
2)when trying to get the downloadable link it cannot find the object
below is my code
AddProduct = () => {
this.setState({ loading: true })
this.setState({ disabled: true })
//-------------UUIDGenerator-------------
UUIDGenerator.getRandomUUID().then((productUid) => {
this.setState({ productUid });
() => console.log("setted state=" + this.state.productUid)
console.log("Generated UUid " + this.state.productUid)
});
//----------------------------------------
//image upload
try {
setTimeout(() => {
const fs = RNFetchBlob.fs
const uid = "flyers/" + this.state.productUid;
const imageRef = firebase.storage().ref(uid).child('abc' + ".jpg") //string "dp1.jpg"
let mime = 'image/jpg'
if (this.state.imagePath === "") {
alert("Select a Flyer")
return
}
//-----------------Checking Inputs-----------
if (this.state.title.length < 1) {
alert("Enter a Title");
return;
}
if (this.state.description.length < 3) {
alert("Enter At least 3 words To describe the product ")
return;
}
if (this.state.storename.length < 1) {
alert("Enter a Storename")
return;
}
alert("Uploading Flyer" + this.state.title)
//------------------------------------------------
this.state.imagePath.forEach((data) => {
// coverting to base64
new Promise((resolve, reject) => {
fs.readFile(data, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
console.log("blob " +JSON.stringify(blob))
//uplaoding Image
uploadBlob = blob
name =name + 1
firebase.storage().ref(uid).child(name + ".jpg").put(blob) // ,{ contentType: mime }
})
.then(() => {
//uploadBlob.close()
//getting url
console.log("1")
return firebase.storage().ref(uid).child(name + ".jpg").getDownloadURL()
})
})
})
.then(() => {
//urls = url;
console.log('urls=' + urls)
//================================
try {
alert("Uploading Flyerss" + this.state.title)
//-------------------------------------------
//----------Inserting Data to Database--------
usersTable = 'flyers/' + this.state.productUid,
console.log(usersTable)
firebase.database().ref(usersTable).set(
{
title: this.state.title,
description: this.state.description,
imageUrls: this.state.imageUrls,
storename: this.state.storename,
user: asyncValue,
longitude: this.state.longitude,
latitutde: this.state.latitutde
}
).then(
console.log("then"),
this.title.clear(),
this.description.clear(),
//this.category.clear(),
this.setState({ loading: false }),
this.setState({ disabled: false }),
this.setState({ title: "" }),
this.setState({ storename: "" }),
this.setState({ description: "" }),
this.setState({ imagePath: "" }),
urls = "",
alert(this.state.title + " flyer sucessfully uploaded")
)
//--------------------------------------------
}
catch (error) {
this.setState({ loading: false })
this.setState({ disabled: false })
console.log(error.toString())
alert(error.toString())
}
//================================
})
}, 3000)
}
catch (error) {
console.log("upload Error = " + error)
}
}
I'm trying to send/upload image file to my back-end serve using fetch multipart upload in react-native, but fetch multipart form data upload is not working for android, however I tried different examples.
Image upload multipart form data API is based on php and its working for iOS react-native app.
I am using react-native-photo-upload library for taking image.
storePicture(PicturePath:string) {
console.warn(PicturePath);
if (PicturePath) {
const apiUrl = `${Constants.APIHOST}updateprofileimage.php`;
// Create the form data object
var data = new FormData();
data.append('profileimage', { uri:PicturePath, name: 'profileimage.jpg', type: 'image/jpg/jpeg' });
data.append('accesstoken', this.state.user.sAccessToken);
data.append('react-native', 1);
// Create the config object for the POST // You typically have an OAuth2 token that you use for authentication
const config = { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'multipart/form-data;' }, body: data };
fetch(apiUrl, config)
.then(responseData => { // Log the response form the server
// Here we get what we sent to Postman back
console.warn(`response:${responseData}`);
})
.catch(err => {
console.warn(err);
});
}}
Here is the example how I am calling storePicture() function.
<PhotoUpload onResizedImageUri={
avatar => {
if (avatar) {
this.storePicture(avatar.path);
}
}}
>
<Image source={{uri: this.state.user.sProfileImageUrl}} style={{resizeMode:"cover", marginTop:8.0, backgroundColor:'transparent', height:120.0, width:120, borderRadius:60.0, borderWidth:0.0, borderColor:'transparent'}}/>
</PhotoUpload>
uploadProfileImage = async (image:var) => {
this.setState({
loading: true
});
var RNFS = require('react-native-fs');
const path = Style.IS_IOS ? image.uri : image.path;
var fileName = path.split('/').pop();
var fileType = fileName.split('.').pop();
var filePath = Style.IS_IOS ? path : 'file://' + path;
const apiURL = `${Constants.APIHOST}updateprofileimage.php`;
const formData = new FormData();
formData.append('accesstoken', this.state.user.sAccessToken);
formData.append('reactnative', 1);
formData.append('profileimage', {
uri:filePath,
name: fileName,
type: `image/${fileType}`,
});
try {
const response = await fetch(apiURL, {
body: formData,
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json',
},
})
const json = await response.json()
this.handleUploadImageResponse(json);
} catch (err) {
this.setState({
loading: false
},console.log('catch Error: '+ err));
}
}
I am answering my own question as I haven't found any valid answer for the sake of other users, who are facing same issue.
Please let me know if I can improve my answer/post or in case any help is needed from me.
Image Upload to an API by Multipart FormData
uploadPicture = () => {
console.log(
"Image Upload urI = " + JSON.stringify(this.state.imageSourceUri.uri)
);
this.setState({ loading: true });
const form = new FormData();
form.append("fileToUpload", {
uri: this.state.imageSourceUri.uri,
type: "image/jpg",
name: "12334"
});
fetch("http://119.82.97.221/HPBSProjectApi2/api/HPBS/PostFormData", {
method: "post",
body: form
})
.then(response => response.json())
.then(response => {
console.log("response = " + response);
this.setState({
loading: false
});
});
};
the problem is the type field in the FormData, use mime to resolve it. Images must be image/jpeg.
const formData = new FormData();
formData.append("image",{..., name, uri, type: mime.getType(uri)}));
I have a react-native app on Android and a backend server written in NodeJS + Express and I'm using multer to handle file uploads.
const multer = require('multer');
const mime = require('mime');
const crypto = require('crypto');
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, config.uploads),
filename: (req, file, cb) => {
crypto.pseudoRandomBytes(16, (err, raw) => {
cb(null, raw.toString('hex') + Date.now() + '.' + mime.extension(file.mimetype));
});
}
});
const upload = multer({ storage });
const Router = require('express').Router;
const controller = require('./upload.controller');
const router = new Router();
const auth = require('./../../auth/auth.service');
router.post('/', [auth.isAuthenticated(), upload.any()], controller.create);
module.exports = router;
And on my react-native app I try to do like this:
ImagePicker.launchCamera(options, image => {
let { uri } = image
const API_URL = 'http://192.168.1.2:9000/api/uploads'
var form = new FormData();
form.append("FormData", true)
form.append("access_token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU3YjgyZGQ2MTEwZDcwYmEwYjUxZjM5YyIsImlzTWVkaWMiOnRydWUsImlhdCI6MTQ3MTY4ODE1MiwiZXhwIjoxNDcxNzA2MTUyfQ.gPeql5g66Am4Txl1WqnbvOWJaD8srTK_6vihOJ6kFbY")
form.append("Content-Type", "image/jpg")
form.append('image', uri)
fetch(API_URL, {body: form, mode: "FormData", method: "post", headers: {"Content-Type": "multipart/form-data"}})
.then((response) => console.log(response))
.catch((error) => {
console.log("ERROR " + error)
})
.then((responseData) => {
console.log("Succes "+ responseData)
})
.done();
})
But when I try to upload I recive the following error
multipart body must have at least one part
I am doing something wrong?
Does anybody knows a better solution to do this?
Fetch may not support Blob and FormData at this moment, but you can use XMLHttpRequest polyfill instead.
let xhr = new XMLHttpRequest()
xhr.open('post', `http://myserver.com/upload-form`)
xhr.send(form)
xhr.onerror = function(e) {
console.log('err', e)
}
xhr.onreadystatechange = function() {
if(this.readyState === this.DONE) {
console.log(this.response)
}
}