I am working on an app and I am using expo, I want to make sure each user can upload an image to firebase, and later publish this image on the profile page.
Using expo this is how I upload images:
const pickImage = async () => {
let pickerResult = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(pickerResult);
handleImagePicked(pickerResult);
};
the result in the console is:
Object {
"cancelled": false,
"height": 312,
"type": "image",
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fallergyn-app-77bfd368-65fd-43f9-8c34-9c35cef42c25/ImagePicker/daaa229c-c352-4994-ae18-ca2dbb3534ce.jpg",
"width": 416,
}
and this is how I upload to the firebase:
const handleImagePicked = async (pickerResult) => {
try {
if (!pickerResult.cancelled) {
setImage(pickerResult.uri);
await uploadImageAsync(pickerResult.uri);
console.log("done");
}
} catch (e) {
console.log(e);
alert("Upload failed, sorry :(");
} finally {
}
};
async function uploadImageAsync(uri) {
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
const ref = firebase
.storage()
.ref()
.child("images" + Math.random());
const snapshot = await ref.put(blob);
// We're done with the blob, close and release it
blob.close();
return await snapshot.ref.getDownloadURL();
}
this code works it saves the path of the image this: "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fallergyn-app-77bfd368-65fd-43f9-8c34-9c35cef42c25/ImagePicker/daaa229c-c352-4994-ae18-ca2dbb3534ce.jpg" in the firebase under user collection using the uid of the user.
I am not sure if this is good, because I want to make sure the image itself is uploaded to firebase, I saw some threads in StackOverflow regarding this issue either too old or no answers, so I am hoping to get some sort of solution to what I need to do.
if I use
const ref = firebase
.storage()
.ref()
.child("images" + Math.random());
.putFile(uri);
this tells me that putFile is not a function. the same with put(uri)
Try this one. This function returns the path of the saved image from firebase which you will store in the user's document instead.
const handleImagePicked = async (pickerResult) => {
if (!pickerResult.cancelled) {
setImage(pickerResult.uri);
const result = await uploadImageAsync(pickerResult.uri);
if(result) {
console.log('success');
//save the result path to firestore user document
return;
}
alert("Upload failed, sorry :(");
}
};
export const uploadImageAsync = async (uri: string) => {
let filename = uri;
if (Platform.OS === 'ios') {
filename = uri.replace('file:', '');
}
const ext = filename.split('.').pop();
const path = `images/${id}.${ext}`;
const ref = firebase.storage().ref(path);
try {
const response = await fetch(filename);
const blob = await response.blob();
await ref.put(blob);
return path;
} catch {
return null;
}
};
This worked for me , using rn-fetch-blob
import launchImageLibrary from 'react-native-image-picker';
import RNFetchBlob from 'rn-fetch-blob';
import storage from '#react-native-firebase/storage';
const pickImage = () => {
let options = {
mediaType: 'photo',
quality: 0.5,
};
launchImageLibrary(options, (response) => {
console.log('Response = ', response);
uploadImagePicked(response);
});
};
const uploadImagePicked = (response) => {
if (response.fileName) {
const fileName = response.fileName;
var storageRef = storage().ref(`receiptImages/${fileName}`);
RNFetchBlob.fs.readFile(response.uri , 'base64')
.then(data => {
storageRef.putString(data, 'base64', {contentType:"image/jpg"})
.on(
storage.TaskEvent.STATE_CHANGED,
snapshot => {
console.log("snapshot: " + snapshot.state);
console.log("progress: " + (snapshot.bytesTransferred / snapshot.totalBytes) * 100);
if (snapshot.state === storage.TaskState.SUCCESS) {
console.log("Success");
}
},
error => {
console.log("image upload error: " + error.toString());
},
() => {
storageRef.getDownloadURL()
.then((downloadUrl) => {
console.log("File available at: " + downloadUrl);
})
})
})
.catch(error => {
console.log(error);
})
}
else {
console.log("Skipping image upload");
}
}
Related
I have a react native project and I need to upload multiple images to firebase storage. in my code below I can select images from the device, and when I upload to the storage, only the first image gets uploaded. I need a way to be able to upload array of images to firebase storage.
I need to upload these images to storage because I will store the array of images in firestore.
const pickImages = async () => {
setIsLoading(true);
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
// allowsEditing: true,
allowsMultipleSelection: true,
selectionLimit: 10,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
setImages(result.uri);
}};
useEffect(() => {
const uploadImage = async () => {
const blobImage = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function () {
reject(new TypeError("network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", images, true);
xhr.send(null);
});
const metadata = {
contentType: "image/jpeg",
};
const storageRef = ref(storage, "Service/" + blobImage.data.name);
const uploadTask = uploadBytesResumable(storageRef, blobImage, metadata);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
}
},
(error) => {
switch (error.code) {
case "storage/unauthorized":
break;
case "storage/canceled":
break;
case "storage/unknown":
// Unknown error occurred, inspect error.serverResponse
break;
}
},
() => {
// Upload completed successfully, now we can get the download URL
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
console.log("File available at", downloadURL);
});
}
);
};
if (images != null) {
uploadImage();
setImages(null);
}}, [images]);
am trying to build a web scraper that downloads all the pdfs in a website. i've written all the logic necessary to do this but for some reason it downloads an empty pdf file which is not suppose to be so, the problem seems to be coming from the downloadFile function when i try to pipe the data which for some reason seems not to be working because i get an empty pdf file after the function is ran. i'll would appreciate it if someone can help me out with this problem, thanks.
here's a sample of my code:
app.js
const fs = require("fs");
const path = require("path");
const cheerio = require("cheerio");
const axiosInstance = require("./getAxios");
const axios = axiosInstance();
const Surl = "https://www.health.gov.ng/";
// linkList sample: "https://www.health.gov.ng/index.php?option=com_content&view=article&id=143&Itemid=512";
let = connectionFailCount = 0;
let linkList = [];
let dlinkList = [];
const getWebsiteLinks = async (Surl) => {
try {
console.log(`Crawling all links from: ${Surl}`);
const response = await axios.get(Surl);
const $ = cheerio.load(response.data);
const ranges = $("a").each(function (idx, el) {
if ($(el).attr("href")) {
return $(el).attr("href");
}
});
for (let index = 0; index < ranges.length; index++) {
let raw_links = $("a")[index].attribs.href;
if (raw_links.startsWith("/")) {
linkList.push(Surl + raw_links);
}
}
if (linkList.length > 0) {
console.log(`Finished crawling links: Found ${linkList.length} links`);
console.log(
"--------------------------------------------------------\n\n"
);
}
return;
} catch (error) {
if (connectionFailCount === 0) {
connectionFailCount += 1;
getWebsiteLinks(Surl);
console.log(`Connection error. \n
Reconnecting to server....`);
} else if (connectionFailCount === 5) {
console.error(`Can not connect to server. Try again later.`);
}
}
};
const downloadLinks = async (linkList) => {
try {
console.log("Crawling links to find pdf links. this may take a while...");
for (const link of linkList) {
const response = await axios.get(link);
// Skip where there's delayed server response
if (response.code === "ECONNRESET") continue;
const $ = cheerio.load(response.data);
$("a").each(function (idx, el) {
if ($(el)?.attr("href")?.endsWith(".pdf")) {
let addr = $(el).attr("href");
let dlink = Surl + addr;
dlinkList.push({
pathName: addr,
url: dlink,
});
}
});
}
console.log(dlinkList);
if (dlinkList.length > 0) {
console.log(`Crawling Finish: Found ${dlinkList.length} pdf links`);
console.log(
"--------------------------------------------------------\n\n"
);
}
} catch (error) {
if (connectionFailCount === 0) {
connectionFailCount += 1;
console.log(`Connection error. \n
Reconnecting to server: ${connectionFailCount} count`);
downloadLinks(linkList);
}
if (connectionFailCount === 3) {
console.error(`Can not connect to server. Try again later.`);
return;
}
// console.error("downloadLinksError: ", error);
}
};
const downloadFiles = async (dlinkList) => {
console.log("Creating directory to save PDF files");
const appRoot = path.dirname(path.resolve(__dirname));
// Had to change and restructure code due to error
const folderName = `PDF/${Surl.split("/").pop()}`;
const subFolderName = Surl.split("/").pop();
try {
if (!fs.existsSync(path.join(appRoot, folderName))) {
fs.mkdirSync(path.join(appRoot, "PDF"));
fs.mkdirSync(path.join(`${appRoot}/PDF`, subFolderName));
}
dlinkList.forEach(async (link) => {
let name = link.pathName;
let url = link.url;
let file = fs
.createWriteStream(
`${appRoot}/${folderName}/${name.split("/").pop()}`,
"utf-8"
)
.on("error", (err) => {
console.error("createWriteStreamError: ", err);
});
try {
console.log("Downloading PDF file...");
const { data } = await axios({
url,
method: "GET",
responseType: "stream",
});
if (data) {
console.log("PDF file Downloaded");
data.pipe(file);
}
} catch (error) {
console.error(error);
}
});
return;
} catch (error) {
console.error("downloadFilesError: ", error);
}
};
(async () => {
await getWebsiteLinks(Surl);
await downloadLinks(linkList);
await downloadFiles(dlinkList);
})();
getAxios.js
const axios = require("axios");
const https = require("https");
module.exports = function () {
const domain = "https://www.health.gov.ng/";
let instance;
if (!instance) {
//create axios instance
instance = axios.create({
baseURL: domain,
timeout: 60000, // Increase time out incase of network delay or delayed server response
maxContentLength: 500 * 1000 * 1000, // Increase maximum response ata length
httpsAgent: new https.Agent({ keepAlive: true }),
headers: { "Content-Type": "application/xml" },
});
}
return instance;
};
I want to download an image from a URL.
const downloadImage = async () => {
const permissions = await StorageAccessFramework.requestDirectoryPermissionsAsync();
if (permissions.granted) {
const uri = permissions.directoryUri;
const files = await StorageAccessFramework.readDirectoryAsync(uri);
try {
await StorageAccessFramework.createFileAsync(permissions.directoryUri,
"myImage", "image/png")
.then((r) => {
console.log(r);
FileSystem.downloadAsync(
"my/image/url",
FileSystem.documentDirectory + "myImage.png"
)
.then(({ uri }) => {
console.log("Finished downloading to ", uri);
})
.catch((error) => {
console.error(error);
});
})
.catch((e) => {
console.log(e);
});
} catch {
console.log(e);
}
alert(`Files inside ${uri}:\n\n${JSON.stringify(files)}`);
}
};
Once the function runs everything goes well. A file with the name myImage.png is created. However, it's size is 0 Bytes and there is no image.
What am I missing?
Any help would be appreciated Thanks!
We can use MediaLibrary and FileSystem for this.
Check the below example,
import * as FileSystem from 'expo-file-system';
import * as Permissions from 'expo-permissions';
import * as MediaLibrary from 'expo-media-library';
import moment from 'moment';
import { Button, View } from 'react-native';
export default function DownloadImage() {
const imageUrl = { uri: "https://media.voguebusiness.com/photos/5ef6493adf1073db3375835d/master/pass/kanye-west-gap-news-voguebus-mert-alas-and-marcus-piggott-june-20-story.jpg" }
const handleDownload = async () => {
let date = moment().format('YYYYMMDDhhmmss')
let fileUri = FileSystem.documentDirectory + `${date}.jpg`;
try {
const res = await FileSystem.downloadAsync(imageUrl.uri, fileUri)
saveFile(res.uri)
} catch (err) {
console.log("FS Err: ", err)
}
}
const saveFile = async (fileUri) => {
const { status } = await Permissions.askAsync(Permissions.MEDIA_LIBRARY);
if (status === "granted") {
try {
const asset = await MediaLibrary.createAssetAsync(fileUri);
const album = await MediaLibrary.getAlbumAsync('Download');
if (album == null) {
await MediaLibrary.createAlbumAsync('Download', asset, false);
} else {
await MediaLibrary.addAssetsToAlbumAsync([asset], album, false);
}
} catch (err) {
console.log("Save err: ", err)
}
} else if (status === "denied") {
alert("please allow permissions to download")
}
}
return (
<View>
<Button
title="Download"
onPress={handleDownload}
/>
</View>
)
}
I'm trying to upload(send) a PDF to server using below code snippet:
const xhr = new XMLHttpRequest();
xhr.open("POST", "/upload");
xhr.onload = (e) => {
const response = JSON.parse(xhr.response);
console.log(response);
};
xhr.onerror = (error) => {
console.log(error);
};
xhr.ontimeout = (e) => {
console.log(e, "upload timeout");
};
const formData = new FormData();
formData.append("fileToUpload", {
uri: fileToUpload.uri,
type: `*/*`,
name: fileToUpload.name,
});
xhr.send(formData);
if (xhr.upload) {
xhr.upload.onprogress = ({ total, loaded }) => {
const uploadProgress = loaded / total;
console.log(uploadProgress);
};
}
In response, I'm receiving following error:
Event {
"isTrusted": false,
}
Any other approach to upload a file (pdf) to server using Expo, would also be appreciated.
Thanks
I figured out, that the actual problem was- In android while selecting a file on Android device, DocumentPicker in Expo doesn't provide the complete path to the file, you have to manage it on your own, As I did below.
import * as DocumentPicker from "expo-document-picker";
...
const [singleFile, setSingleFile] = useState(null);
const onSubmit = async () => {
try {
// upload the file
const formData = new FormData();
formData.append("fileToUpload", singleFile);
axios.defaults.headers.post["Content-Type"] =
"multipart/form-data";
const uploadResp = await axios.post(
"/upload",
formData
);
if (uploadResp.status === 200) {
// file uploaded successfully
}
} catch (error) {
}
};
const selectFile = async () => {
await DocumentPicker.getDocumentAsync({
type: "application/pdf",
copyToCacheDirectory: true,
})
.then((response) => {
if (response.type == "success") {
let { name, size, uri } = response;
// >>>>>>>>>>>>> the bug's solution <<<<<<<<<<<<<<<
if (Platform.OS === "android" && uri[0] === "/") {
uri = `file://${uri}`;
uri = uri.replace(/%/g, "%25");
}
let nameParts = name.split(".");
let fileType = nameParts[nameParts.length - 1];
setSingleFile({
name: name,
size: size,
uri: uri,
type: "application/" + fileType,
});
} else {
setSingleFile(null);
}
})
.catch((err) => {
console.error(err);
});
};
...
<TouchableOpacity activeOpacity={0.6} onPress={() => selectFile()}>
<View >
<Text >
Select File
</Text>
</View>
</TouchableOpacity>
...
My method does manage to load the image from expo to firebase storage but I can't seem to get the download URL.
const uploadImage = async (uri) => {
const uniqid = () => Math.random().toString(36).substr(2, 9);
const ext = uri.split('.').pop(); // Extract image extension
const filename = `${uniqid()}.${ext}`; // Generate unique name
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child('images/' + filename);
ref.getDownloadURL().then((url) => console.log(url));
return ref.put(blob);
};
Here is the error I get
FirebaseStorageError {
"code_": "storage/object-not-found",
"message_": "Firebase Storage: Object 'images/gebwu7tnh.jpg' does not exist.",
"name_": "FirebaseError",
"serverResponse_": "{
"error": {
"code": 404,
"message": "Not Found. Could not get object",
"status": "GET_OBJECT"
}
}"
This is what I found that helped me from researching. I did refactor my firebase to use a higher order component. Here is my firebase method.
uploadImageAsync: async (uri) => {
const uniqid = () => Math.random().toString(36).substr(2, 9);
const ext = uri.split('.').pop(); // Extract image extension
const filename = `${uniqid()}.${ext}`; // Generate unique name
const ref = firebase
.storage()
.ref()
.child('images/' + filename);
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError('Network request failed'));
};
xhr.responseType = 'blob';
xhr.open('GET', uri, true);
xhr.send(null);
});
const snapshot = await ref.put(blob);
blob.close();
const imgUrl = await snapshot.ref.getDownloadURL();
console.log(imgUrl);
return imgUrl;
},
};
Here is how I implemented it in my component
const setImage = async (uri) => {
try {
return await firebase.uploadImageAsync(uri);
} catch (error) {
console.log(error);
}
};