I have a react native app that uses Firebase, firestore.
for uploading images i am using "react-native-fetch-blob" to create a Blob.
in the js file that I use to upload the file, my code look like this:
const Blob = RNFetchBlob.polyfill.Blob
const fs = RNFetchBlob.fs
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
window.Blob = Blob
**
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
**
because of this window.XMLHttpRequest my app is blocked and not getting any response from firebase(not catch / nothing => just passing thrue the code).
if i removed this line i can read/write to the firestore, bat I can't upload an image.
is there anything i can do for uploading images and keep writing to firestore?
Heare is my page:
import ImagePicker from 'react-native-image-crop-picker';
import RNFetchBlob from 'react-native-fetch-blob'
import firebase from 'firebase';
const Blob = RNFetchBlob.polyfill.Blob
const fs = RNFetchBlob.fs
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
window.Blob = Blob
export const choozFile = (isSmalImg) => {
let options = {
width: isSmalImg ? 100 : 690,
height: isSmalImg ? 100 : 390,
cropping: true,
mediaType: 'photo'
};
return new Promise((resolve, reject) => {
ImagePicker.openPicker(options).then(response => {
let source = { uri: response.path };
resolve({ avatarSource: source, isProfileImg: isSmalImg })
})
});
}
export const addReportToFirebase = (obj = {}, uri, isProfile, mime = 'application/octet-stream') => {
obj["uId"] = "JtXNfy34BNRfCoRO6luwhIJke0l2";
const storage = firebase.storage();
const db = firebase.firestore();
const uploadUri = uri;
const sessionId = new Date().getTime();
let uploadBlob = null;
const imageRef = storage.ref(`images${isProfile ? '/profile' : ''}`).child(`${sessionId}`)
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()
imageRef.getDownloadURL().then((url)=>{
obj['image'] = url;
db.collection("reports").add(obj).then(() => {
console.log("Document successfully written!");
}).catch((err) => {
console.error("Error writing document: ", err);
});
})
})
.catch((error) => {
console.log('upload Image error: ', error)
})
};
I had same issue , i did some trick to resolve this. This might not be most correct solution but it worked for me.
Trick is keep RNFetchBlob.polyfill.XMLHttpRequest in window.XMLHttpRequest only for the upload operation. Once you done with uploading image to storage revert window.XMLHttpRequest to original value.
your code will look like this.
import ImagePicker from 'react-native-image-crop-picker';
import RNFetchBlob from 'react-native-fetch-blob'
import firebase from 'firebase';
const Blob = RNFetchBlob.polyfill.Blob
const fs = RNFetchBlob.fs
window.Blob = Blob
export const choozFile = (isSmalImg) => {
let options = {
width: isSmalImg ? 100 : 690,
height: isSmalImg ? 100 : 390,
cropping: true,
mediaType: 'photo'
};
return new Promise((resolve, reject) => {
ImagePicker.openPicker(options).then(response => {
let source = { uri: response.path };
resolve({ avatarSource: source, isProfileImg: isSmalImg })
})
});
}
export const addReportToFirebase = (obj = {}, uri, isProfile, mime = 'application/octet-stream') => {
obj["uId"] = "JtXNfy34BNRfCoRO6luwhIJke0l2";
const storage = firebase.storage();
const db = firebase.firestore();
const uploadUri = uri;
const sessionId = new Date().getTime();
let uploadBlob = null;
//keep reference to original value
const originalXMLHttpRequest = window.XMLHttpRequest;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
const imageRef = storage.ref(`images${isProfile ? '/profile' : ''}`).child(`${sessionId}`)
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();
//revert value to original
window.XMLHttpRequest = originalXMLHttpRequest ;
imageRef.getDownloadURL().then((url)=>{
obj['image'] = url;
db.collection("reports").add(obj).then(() => {
console.log("Document successfully written!");
}).catch((err) => {
console.error("Error writing document: ", err);
});
})
})
.catch((error) => {
console.log('upload Image error: ', error)
})
};
that simple,,u can try this to upload image
<i>
getSelectedImages = (selectedImages, currentImage)=>{
const image = this.state.uri
let uploadBlob = null
let mime = 'image/jpg'
const originalXMLHttpRequest = window.XMLHttpRequest;
const originalBlob = window.Blob;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
window.Blob = RNFetchBlob.polyfill.Blob
const imageRef = firebase.storage().ref('posts').child(this.state.uri)
RNFetchBlob.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()
window.XMLHttpRequest = originalXMLHttpRequest ;
window.Blob = originalBlob
return imageRef.getDownloadURL()
})
.then((url) => {
firebase.database().ref('groub').child(params.chat).child('message').push({
createdAt:firebase.database.ServerValue.TIMESTAMP,
image:url,
user:{
_id:params.id,
name:params.name,
},
})
alert('Upload Sukses')
})
.catch((error) => {
console.log(error);
})
}
</i>
Related
I'm looking for a solution/module where I don't need to inject inital/fallback data for swr/react-query things from getServerSideProps. Like...
from
// fetcher.ts
export default fetcher = async (url: string) => {
return await fetch(url)
.then(res => res.json())
}
// getUserData.ts
export default function getUserData() {
return fetcher('/api')
}
// index.tsx
const Page = (props: {
// I know this typing doesn't work, only to deliver my intention
userData: Awaited<ReturnType<typeof getServerSideProps>>['props']
}) => {
const { data } = useSWR('/api', fetcher, {
fallbackData: props.userData,
})
// ...SSR with data...
}
export const getServerSideProps = async (ctx: ...) => {
const userData = await getUserData()
return {
props: {
userData,
},
}
}
to
// useUserData.ts
const fetcher = async (url: string) => {
return await fetch(url)
.then(res => res.json())
};
const url = '/api';
function useUserData() {
let fallbackData: Awaited<ReturnType<typeof fetcher>>;
if (typeof window === 'undefined') {
fallbackData = await fetcher(url);
}
const data = useSWR(
url,
fetcher,
{
fallbackData: fallbackData!,
}
);
return data;
}
// index.tsx
const Page = () => {
const data = useUserData()
// ...SSR with data...
}
My goal is making things related to userData modularized into a component.
I'm trying to upload a file with AngularFireStorage service. File is uploaded and I can get download URL, but I can't pass status(progress,downloadurl) to nz-upload component. Is there someone solves it? I think S3 way may look like similar.
uploadFile = (item: UploadXHRArgs) => {
console.log('call uploadFile');
console.log(item);
const file = item.file;
const filePath = `${this.authService.user.uid}/${file.uid}`;
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(filePath, file)
return task.snapshotChanges().pipe(
finalize(() => {
fileRef.getDownloadURL().subscribe(result => {
console.log(result);
});
})
)
.subscribe();
}
handleChange({ file, fileList }: UploadChangeParam): void {
console.log(file.status);
const status = file.status;
if (status !== 'uploading') {
console.log(file, fileList);
}
if (status === 'done') {
this.msg.success(`${file.name} file uploaded successfully.`);
} else if (status === 'error') {
this.msg.error(`${file.name} file upload failed.`);
}
}
In browser console
call uploadFile
Object { action: "", name: "file", headers: undefined, file: File, postFile: File, data: undefined, withCredentials: false, onProgress: onProgress(e), onSuccess: onSuccess(ret, xhr), onError: onError(xhr)
}
uploading
https://firebasestorage.googleapis.com/v0/b/xxxx.appspot.com/o/bc7Q7zMxCWdJW0FtHrWtC0y6Vle2%2Fmnjvjqua0z?alt=media&token=6a50e16d-2b42-43b3-907a-add7f7a9b8f6
On a page
Solved, thanks to https://github.com/ezhuo/ngx-alain/blob/master/src/app/#core/utils/image.compress.service.ts#L123
uploadFile = (item: UploadXHRArgs) => {
const file = item.file;
const filePath = `${this.authService.user.uid}/${file.uid}`;
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(filePath, file);
return task.snapshotChanges().pipe(
finalize(() => {
fileRef.getDownloadURL().subscribe(result => {
item.onSuccess(result, item.file, result);
});
})
)
.subscribe(
(result) => {
const event = { percent: 0};
event.percent = (result.bytesTransferred / result.totalBytes) * 100;
item.onProgress(event, item.file);
},
err => {
item.onError(err, item.file);
}
);
}
P.S. Still trying to understand item.onSuccess(result, item.file, result); but upload and preview and progress, works.
I'm trying to upload a Picture form my Phone to Firebase using Expo.
I get a uri form the Picture but not sure how to convert it, that I can uploade it to Firebase?
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [4, 3],
});
if (!result.cancelled) {
console.log('device URL: w',result.uri);
this.setState({ image: result.uri });
this.uploadImage(result.uri).then(resp =>{
alert('success')
}).catch(err=>{
console.log(err)
})
}
};
When i Log result.uri I get:
file:///var/mobile/Containers/Data/Application/1E5612D6-ECDB-44F4-9839-3717146FBD3E/Library/Caches/ExponentExperienceData/%2540anonymous%252FexpoApp-87f4a5f5-b117-462a-b147-cab242b0a916/ImagePicker/45FA4A7B-C174-4BC9-B35A-A640049C2CCB.jpg
How can I convert it to a format that works for firebase?
you can convert the image to a base64, there are several libraries that can do that.
You need to convert the image to a base64, here is an example using rn-fetch-blob
https://github.com/joltup/rn-fetch-blob
export const picture = (uri, mime = 'application/octet-stream') => {
//const mime = 'image/jpg';
const { currentUser } = firebase.auth();
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
return ((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
let uploadBlob = null;
const imageRef = firebase.storage().ref('your_ref').child('child_ref');
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` });
})
.then((blob) => {
uploadBlob = blob;
imageRef.put(blob._ref, blob, { contentType: mime });
})
.then(() => {
//take the downloadUrl in case you want to downlaod
imageRef.getDownloadURL().then(url => {
// do something
});
});
});
};
When I try to upload the generated PDF file to storage bucket, the firebase logs gives me that response after log "Init upload of file...":
Function execution took 3721 ms, finished with status: 'connection
error'
Maybe the problem can be the order of Promises. But I'm beginner with Cloud Functions and Node.js to reorder that.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const pdf = require('html-pdf');
const gcs = require('#google-cloud/storage')({keyFilename: './service_account.json'});
const handlebars = require('handlebars');
const path = require('path');
const os = require('os');
const fs = require('fs');
const bucket = gcs.bucket(bucketURL);
admin.initializeApp(functions.config().firebase);
var html = null;
exports.generatePdf = functions.https.onRequest((request, response) => {
// data to apply to template file
const user = {
"date": new Date().toISOString(),
"title": "Voucher",
"body": "Voucher body"
};
const options = {
"format": 'A4',
"orientation": "portrait"
};
const localPDFFile = path.join(os.tmpdir(), 'localPDFFile.pdf');
try {
const source = fs.readFileSync(__dirname + '/voucher.html', 'utf8');
html = handlebars.compile(source)(user);
} catch (error) {
console.error(error);
}
const phantomJsCloud = require("phantomjscloud");
const browser = new phantomJsCloud.BrowserApi(phantomApiKey);
var pageRequest = { content: html, renderType: "pdf" };
// // Send our HTML to PhantomJS to convert to PDF
return browser.requestSingle(pageRequest)
.then(function (userResponse) {
if (userResponse.statusCode !== 200) {
console.log("invalid status code" + userResponse.statusCode);
} else {
console.log('Successfully generated PDF');
// Save the PDF locally
fs.writeFile(localPDFFile, userResponse.content.data, {
encoding: userResponse.content.encoding,
}, function (err) {
console.log('Init upload of file...' + localPDFFile);
// Upload the file to our cloud bucket
return bucket.upload(localPDFFile, {
destination: '/pdfs/voucher.pdf',
metadata: {
contentType: 'application/pdf'
}
}).then(() => {
console.log('bucket upload complete: ' + localPDFFile);
response.status(200).send({
message: 'PDF Gerado com sucesso!',
address: localPDFFile
});
return true;
}).catch(error => {
response.status(400).send({
message: 'Error on bucket upload!',
error: error
});
return false;
});
});
return true;
}
return true;
});
})
I used this block of code to select an image when button is pressed to upload to firebase.
<TouchableHighlight onPress={ () => this._pickImage() }
style={styles.button}>
<Text
style={styles.buttonText}>
Select a Photo
</Text>
</TouchableHighlight>
_pickImage() {
this.setState({ uploadURL: '' })
ImagePicker.launchImageLibrary({}, response => {
uploadImage(response.uri)
.then(url => this.setState({ uploadURL: url }))
.catch(error => console.log(error))
});
}// end _pickImage
const uploadImage = (uri, mime = 'application/octet-stream') => {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
const sessionId = new Date().getTime()
let uploadBlob = null
const imageRef = storage.ref('images').child(`${sessionId}`)
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)
})
})
}
This works fine. How can I change this to let the user select photo and then hit another button store additional info for the image like Description entered by the user into the same data structure in firebase?