How to upload multiple image to firebase in react native fetch blob - javascript

I have multiple images that are stored in an array (image paths stored in array).
then I use a for loop to upload every image, but only the last image is uploaded.
I use react native fetch blob, and firebase
for(var i = 0; i < this.state.imagesUri;i++){
Blob.build(RNFetchBlob.wrap(this.state.imagesUri[i].path),{ type : 'image/jpeg' })
.then((blob) => firebase.storage()
.ref("userPhoto").child("image"+i)
.put(blob, { contentType : 'image/png' }).then(()=>{
var storage = firebase.storage().ref("userPhoto/").child("image"+i);
storage.getDownloadURL().then((url)=>{
var url = url;
});
})
);
}

i hope this will help
onSend(images) {
let photo = images.map( img=> img.image);
photo.forEach((image, i) => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest =
RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = this.image.child(`${sessionId}${i}`);
fs.readFile(image, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL()
})
.then((url) => {
console.log(url)
})
.catch((error) => {
});
})
}

OK, first of all, you need to cache the length of the array this.state.imagesUri.
This will make your for loop look like so for(var i = 0, length = this.state.imagesUri.length; i < length;i++){, I hope you've noticed that you aren't checking anymore if the i < this.state.imagesUri (This is incorrect since imagesUri is an array).

I have this code. To upload multi image with firebase and 'rn-fetch-blob' on react native
export const uploadListImageMeal = (param) => {
const { imagesUrls, mime = 'application/octet-stream', userID, time } = param
const urls = imagesUrls.map((uri) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
// let uploadBlob = null
const currentTime = Date.now()
const imageRef = firebase.storage().ref(`images/${userID}/meal/${time}`).child(`${currentTime}.png`)
return fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
// uploadBlob = blob
return imageRef.put(blob._ref, blob, { contentType: mime })
})
.then(() => {
// uploadBlob.close()
return imageRef.getDownloadURL()
})
.then((url) => {
return (url)
})
.catch((error) => {
return host
})
})
return Promise.all(urls)
.then((data) => {
return data
})
}

Related

Conversion to native javascript returns destructuring error [duplicate]

This question already has answers here:
Convert from Ajax to Axios
(2 answers)
Closed last year.
The following script is a conversion of a TypeScript to native Javascript so that it may run inline.
It is subordinated to the zxing library loaded via importmaps
<script type="importmap" data-turbo-track="reload">{
"imports": {
[...]
"#zxing/library": "https://ga.jspm.io/npm:#zxing/library#0.19.1/esm/index.js",
"ts-custom-error": "https://ga.jspm.io/npm:ts-custom-error#3.2.0/dist/custom-error.js",
[...]
}
It returns the error: Uncaught SyntaxError: Invalid destructuring assignment target
for a line that indicates:
url: "http://localhost:3000/articlephotos/barcode_section_update",
One additional edit was made to convert a jQuery function to run independantly of jQuery:
$.ajax({. to function ajax({
What is mistaken in this script?
window.addEventListener("turbo:load", function() {
let selectedDeviceId
const codeReader = new ZXing.BrowserMultiFormatReader()
console.log("ZXing code reader initialized")
codeReader
.getVideoInputDevices()
.then(videoInputDevices => {
const sourceSelect = document.getElementById("sourceSelect")
selectedDeviceId = videoInputDevices[0].deviceId
if (videoInputDevices.length >= 1) {
videoInputDevices.forEach(element => {
const sourceOption = document.createElement("option")
sourceOption.text = element.label
sourceOption.value = element.deviceId
sourceSelect.appendChild(sourceOption)
})
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value
}
const sourceSelectPanel = document.getElementById("sourceSelectPanel")
sourceSelectPanel.style.display = "block"
}
document.getElementById("startButton").addEventListener("click", () => {
codeReader.decodeFromVideoDevice(
selectedDeviceId,
"video",
(result, err) => {
if (result) {
console.log(result)
document.getElementById("result").textContent = result.text
let formData = new FormData()
let CodeParams = {
code_data: result.text
}
formData.append("code_json_data", JSON.stringify(CodeParams))
function ajax({
url: "http://localhost:3000/articlephotos/barcode_section_update",
type: "post",
data: formData,
processData: false,
contentType: false
})
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err)
document.getElementById("result").textContent = err
}
}
)
console.log(
`Started continous decode from camera with id ${selectedDeviceId}`
)
})
document.getElementById("resetButton").addEventListener("click", () => {
codeReader.reset()
document.getElementById("result").textContent = ""
console.log("Reset.")
})
})
.catch(err => {
console.error(err)
})
})
Posting an answer here for who comes around to it.
The suggestion from #PeterKrebs of using axios is pertinent. Note however some syntaxic precisions, as following the links might leand one to use axios.post when this.axios.post(...) is necessary.
The script ends up very similar to the jQuery AJAX notation:
window.addEventListener("turbo:load", function() {
let selectedDeviceId
const codeReader = new ZXing.BrowserMultiFormatReader()
console.log("ZXing code reader initialized")
codeReader
.getVideoInputDevices()
.then(videoInputDevices => {
const sourceSelect = document.getElementById("sourceSelect")
selectedDeviceId = videoInputDevices[0].deviceId
if (videoInputDevices.length >= 1) {
videoInputDevices.forEach(element => {
const sourceOption = document.createElement("option")
sourceOption.text = element.label
sourceOption.value = element.deviceId
sourceSelect.appendChild(sourceOption)
})
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value
}
const sourceSelectPanel = document.getElementById("sourceSelectPanel")
sourceSelectPanel.style.display = "block"
}
document.getElementById("startButton").addEventListener("click", () => {
codeReader.decodeFromVideoDevice(
selectedDeviceId,
"video",
(result, err) => {
if (result) {
console.log(result)
document.getElementById("result").textContent = result.text
let formData = new FormData()
let CodeParams = {
code_data: result.text
}
formData.append("code_json_data", JSON.stringify(CodeParams))
this.axios.post('<%= barcode_section_update_articlephotos_url %>', {
data: formData,
processData: false,
contentType: false
})
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err)
document.getElementById("result").textContent = err
}
}
)
console.log(
`Started continous decode from camera with id ${selectedDeviceId}`
)
})
document.getElementById("resetButton").addEventListener("click", () => {
codeReader.reset()
document.getElementById("result").textContent = ""
console.log("Reset.")
})
})
.catch(err => {
console.error(err)
})
})

How to push a file in a base64 format in array?

I am trying to push to the data array the object with the file and file base64 format in getBase64.
But when I console log data array it is empty, also I can turn file into base64 format. But I don't understand why the push method in getBase64 doesn't work. How do I get bot the file and file base 64.
export default function UploadDoc({ setInputs }) {
function beforeUpload(file) {
const isRightType =
file?.type === "application/pdf" ||
file?.type === "application/xlsx" ||
file?.type === "image/jpeg" ||
file?.type === "application/csv" ||
file?.type === "text/plain";
if (!isRightType) {
message.error("You can only upload PDF, TXT, JPEG, CSV or XLSX files!");
}
const isLt2M = file?.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("PDF must be smaller than 2MB!");
}
setFileList((state) => (state ? [...state, file] : [file]));
return isRightType && isLt2M;
}
const overrideRequest = ({ file, onSuccess, onError }) => {
// <Upload/> renders another component (rc-upload) as its child which handles the actual AJAX upload.
// You can override this behaviour by passing a customRequest prop to <Upload/>
const isRightType =
file?.type === "application/pdf" ||
file?.type === "image/jpeg" ||
file?.type === "application/csv" ||
file?.type === "text/plain";
if (isRightType) {
setTimeout(() => {
onSuccess("ok");
}, 1);
} else {
setTimeout(() => {
onError("ok");
}, 1);
}
};
const getBase64 = (file) =>
new Promise((resolve) => {
// turns file data in loggable and readable data from javascript
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(file);
});
const dispatch = useDispatch();
const fileList = useSelector((state) => state.filesFileListReducer);
const handleUploadChange = (info) => {
if (info.file.status === "error") {
console.log("error", info.file);
}
if (info.file.status === "done") {
// Get this url/data from response in real world.
const arr = [];
info.fileList.forEach(async (file) => {
const fileInfo = await getBase64(file.originFileObj);
const infoOnUploadedFile = { base64: fileInfo, fileObj: file };
console.log("file info", infoOnUploadedFile);
arr.push(infoOnUploadedFile);
});
console.log("data", arr);
}
};
return (
<Upload
{...{
multiple: true,
beforeUpload: (file) => beforeUpload(file),
accept: ".txt, .csv, .jpeg, .pdf",
customRequest: (info) => overrideRequest(info),
onChange: (info) => handleUploadChange(info),
onRemove: (file) => {
const newFileList = fileList?.filter(
(fileGrp) => fileGrp.fileObj !== file
);
dispatch(setFileList(newFileList));
setInputs((state) => ({
...state,
docs: newFileList && newFileList.map((file) => file?.fileBase64),
}));
},
}}
>
<Button icon={<UploadOutlined />} style={{ width: "100px" }} />
</Upload>
);
}
You are trying to get an object before his availability.
Such as you want to get the value but directly, You can use async/await feature (wiki).
For you, something like this will fix it :
const getBase64 = (blob: Blob) => {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onloadend = () => resolve(reader.result)
reader.readAsDataURL(blob) // read blob
})
}
const obj = await getBase64(resBlob); // wait until method not finished
// now you can check if obj is defined and push it into array

Parse-server sharp package in beforeSaveFile

I want to use this package: sharp in beforeSaveFile trigger but it doesn't work. beforeSaveFile doesn't change anything.
My codes:
Parse.Cloud.define('test', async (req) => {
try {
const Resim = Parse.Object.extend('Resim')
const obj = new Resim()
const { photo } = req.params
let uploadedFile = await new Parse.File(
'galleryFile',
{ base64: photo },
'image/png'
)
obj.set('photo', uploadedFile)
const data = await obj.save()
return data
} catch (error) {
throw error
}
})
Parse.Cloud.beforeSaveFile(async (req) => {
const image = await sharp(req.file).resize(256)
return image
})
Thanks for help.
I figured out. This is the solution:
Parse.Cloud.beforeSaveFile(async (req) => {
const file = req.file
const fileData = await file.getData()
const str = fileData.toString('base64')
const imageBuffer = Buffer.from(str, 'base64')
const newImageBuffer = await sharp(imageBuffer)
.resize(800, 800)
.webp({ quality: 70, lossless: true })
.toBuffer()
return new Parse.File(
'image',
{ base64: newImageBuffer.toString('base64') },
'image/webp'
)
})

Upload data to firebase storage and Firebase database from array in react native

I have an array that contains the local location of some images as shown below.
imagePath=
["file:///data/user/0/com.app.taamoutlet/cache/react-native-image-crop-picker/22536474236950.png","file:///data/user/0/com.app.taamoutlet/cache/react-native-image-crop-picker/22583225016770.png
"]
I have done uploading for a single image.
As per the code you have seen below I want to upload the images to firebase storage. I want to upload each image in the array to a single folder. And then collect the downloadable URL and each image and store that information under a single product as you see
() => {
const fs = RNFetchBlob.fs
const uid = "flyers/" + this.state.productUid;
const imageRef = firebase.storage().ref(uid).child(Math.random()+".jpg") //string "dp1.jpg"
let mime = 'image/jpg'
//------------------------------------------------
// coverting to base64
fs.readFile(this.state.imagePath, 'base64')
.then((data) => {
//console.log('data='+data);
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
//uplaoding Image
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
//getting url
return imageRef.getDownloadURL()
})
.then((url) => {
urls = url;
console.log('urls=' + urls)
//================================
try {
alert("Uploading Flyer" + 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: url,
storename: this.state.storename,
user: asyncValue,
longitude: this.state.longitude,
latitutde: this.state.latitutde
}
).then(
alert(this.state.title + " flyer sucessfully uploaded")
)
//--------------------------------------------
}
catch (error) {
this.setState({ loading: false })
this.setState({ disabled: false })
console.log(error.toString())
alert(error.toString())
}
//================================
})
}
As mentioned by Renaud, you have to use Promise.all. Please check out this example:
const promises = [fs.readFile(this.state.imagePath, 'base64'),...];
return Promise.all(promises).then((arrayOfResults) => {
const furtherPromises = [...]
return Promise.all(furtherPromises)
.then((uploadedFiles) => {
// action required
})
.catch((error) => {
// action required
});
}).catch((error) => {
// action required
});

Convert image path to blob react native

Problem
I am trying to create an app with react native and firebase. One of the features I would like for this app is the ability to upload images. I am having some trouble uploading the images to firebase storage though. I am using expo's image picker to find the path of the image that the user wants to upload, but once I have the path I don't know how to convert that to something I can upload to firebase.
Can somebody help me convert the path of an image to something I can upload to firebase storage with react native?
What I've tried
I tried using:
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
MediaTypeOptions: 'Images',
quality: 0.4,
_uploadAsByteArray = async (pickerResultAsByteArray, progressCallback) => {
try {
var metadata = {
contentType: 'image/jpeg',
};
var storageRef = firebase.storage().ref();
var ref = storageRef.child('images/'+expoID+'/'+this.state.time)
let uploadTask = ref.put(pickerResultAsByteArray, metadata)
uploadTask.on('state_changed', function (snapshot) {
progressCallback && progressCallback(snapshot.bytesTransferred / snapshot.totalBytes)
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
}, function (error) {
console.log("in _uploadAsByteArray ", error)
}, function () {
var downloadURL = uploadTask.snapshot.downloadURL;
console.log("_uploadAsByteArray ", uploadTask.snapshot.downloadURL)
this.setState({imageUploaded:true})
});
} catch (ee) {
console.log("when trying to load _uploadAsByteArray ", ee)
}
}
convertToByteArray = (input) => {
var binary_string = this.atob(input);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes
}
atob = (input) => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let str = input.replace(/=+$/, '');
let output = '';
if (str.length % 4 == 1) {
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (let bc = 0, bs = 0, buffer, i = 0;
buffer = str.charAt(i++);
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
buffer = chars.indexOf(buffer);
}
return output;
}
uploadImage(bsfdata){
this.setState({imageUploaded:false})
this._uploadAsByteArray(this.convertToByteArray(bsfdata), (progress) => {
this.setState({ progress:progress })
})
}
base64:true,
});
/* if (!result.cancelled) {
this.setState({ image: result.uri });
let formData = new FormData();
formData.append('photo', {
uri,
name: `photo.${fileType}`,
type: `image/${fileType}`,
});}*/
this.uploadImage(result.base64);
};
}
I've tried it with the commented code added, which doesn't upload anything, and I've tried it with how the code is now, which gives me the error Can currently only create a Blob from other Blobs, and the uploading progress never gets above 0%.
If you are using expo (>=26), then you can do it easily with the following lines of code.
uploadImage = async(imageUri) => {
const response = await fetch(imageUri);
const blob = await response.blob();
var ref = firebase.storage().ref().child("image.jpg");
return ref.put(blob);
}
Reference: https://youtu.be/KkZckepfm2Q
Refer this link - https://github.com/dailydrip/react-native-firebase-storage/blob/master/src/App.js#L43-L69
Following block of code is working fine.
uploadImage(uri, mime = 'application/octet-stream') {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
let uploadBlob = null
const imageRef = FirebaseClient.storage().ref('images').child('image_001')
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
return imageRef.getDownloadURL()
})
.then((url) => {
resolve(url)
})
.catch((error) => {
reject(error)
})
})
}
You need to install rn-fetch-blob module:
npm install --save rn-fetch-blob
Then, do the following:
import RNFetchBlob from 'rn-fetch-blob';
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
function uploadImage(path) {
const imageFile = RNFetchBlob.wrap(path);
// 'path/to/image' is where you wish to put your image in
// the database, if you would like to put it in the folder
// 'subfolder' inside 'mainFolder' and name it 'myImage', just
// replace it with 'mainFolder/subfolder/myImage'
const ref = firebase.storage().ref('path/to/image');
var uploadBlob = null;
Blob.build(imageFile, { type: 'image/jpg;' })
.then((imageBlob) => {
uploadBlob = imageBlob;
return ref.put(imageBlob, { contentType: 'image/jpg' });
})
.then(() => {
uploadBlob.close();
return ref.getDownloadURL();
})
.((url) => {
// do something with the url if you wish to
})
.catch(() => {
dispatch({
type: UPDATE_PROFILE_INFO_FAIL,
payload: 'Unable to upload profile picture, please try again'
});
});
}
Please do ask if there's any part of the code that you don't understand. To upload multiple images, simply wrap this code with a for loop. Or if you want to make sure that every image is uploaded without any error, use Promise
Not sure whom this might help, but if you're using MediaLibrary to load images from the gallery, then the uri comes in the format of uri = file:///storage/emulated/0/DCIM/Camera/filename.jpg
In this case, using fetch(uri) didn't help me get the blob.
But if you use fetch(uri.replace("file:///","file:/")) and then follow #sriteja Sugoor's answer, you'll be able to upload the file blob.
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
let uploadBlob;
await fs
.readFile(params?.file.path, 'base64')
.then((data) => {
return Blob.build(data, {type: `BASE64`});
})
.then((blob) => {
uploadBlob = blob;
console.log(uploadBlob, 'uploadBlob');
});

Categories

Resources