Uploading an mp3 to Firebase Storage with React Native Expo - javascript

I am attempting to upload an mp3 to firebase storage using expo and react native. So far I've got the file into firebase storage, but it's only 9bytes large, so I'm doing something wrong. I've attempted this with blob as shown below with no success.
Here is a screenshot of the firebase storage folder showing the file uploaded but not the data of said file:
Any help is greatly appreciated, I feel like I'm missing a step to actually upload the data along with the file.
export default function SongPicker() {
const [song, setSong] = useState(null);
//Get current user through authentication
const user = auth.currentUser;
const pickDocument = async () => {
let result = await DocumentPicker.getDocumentAsync({});
// Fetch the photo with it's local URI
const response = fetch(result.uri);
alert(result.uri);
console.log(result);
const file = new Blob(
[response.value], {
type: 'audio/mpeg'
});
console.log('do we see this?');
try {
//Create the file reference
const storage = getStorage();
const storageRef = ref(storage, `songs/${user.uid}/${result.name}`);
// Upload Blob file to Firebase
const snapshot = uploadBytes(storageRef, file, 'blob').then((snapshot) => {
console.log('Uploaded a song to firebase storage!');
});
setSong(result.uri);
} catch (error) {
console.log(error);
}
}

The fetch() returns a Promise so you should add an await for that as well.
const response = await fetch(result.uri);
Then try using blob() method on the Response:
const file = await response.blob()
The third param in uploadBytes should be upload metadata object but you can skip that here:
const snapshot = await uploadBytes(storageRef, file).

Related

Firebase Storage upload always times out ("storage/retry-limit-exceeded" error)

I'm writing a script using the Firebase JavaScript (client, not admin) SDK to handle uploading a large quantity of images (~1.5 mb each) from an external download link to Google Firebase Storage, and I cannot successfully upload a single image.
Here's the piece of code that I'm testing out with a single image URL:
const firebaseConfig = {
// firebase config details
};
const app = initializeApp(firebaseConfig);
const storage = getStorage(app);
storage.maxOperationRetryTime = 120000;
storage.maxUploadRetryTime = 120000;
const addImageToFirebase = async (url) => {
const download = await fetch(url);
const blob = await download.blob();
const path = "test-folder/test-file.png";
const imageRef = ref(storage, path);
try {
await uploadBytes(imageRef, blob);
console.log('success!');
} catch (error) {
console.log(error);
}
}
const imageUrl = // download link for test url ;
addImageToFirebase(imageUrl);
When running the code, I get the following error in the terminal, no matter how long I set the maxOperationRetyTime or maxUploadRetryTime:
[StorageError [FirebaseError]: Firebase Storage: Max retry time for operation exceeded, please try again. (storage/retry-limit-exceeded)] {
code: 'storage/retry-limit-exceeded',
customData: { serverResponse: null },
_baseMessage: 'Firebase Storage: Max retry time for operation exceeded, please try again. (storage/retry-limit-exceeded)'
}
Does anyone know why the upload would fail to go through?

how to get data URL from upload bytes in firesbase?

How to get data URL from snapshot after uploading to storage in firebase, I cannot find the link in the snapshot, there must be another method to complete the action.
if (input?.files![0]) {
const storage = getStorage();
const storageRef = ref(storage, `profiles/${_authContext.currentUser.uid}/${input?.files![0].name}`);
// 'file' comes from the Blob or File API
uploadBytes(storageRef, input?.files![0]).then((snapshot) => {
console.log(snapshot);
console.log('Uploaded a blob or file!');
});
}
There is another thing you have to do. It's covered in the documentation. Use getDownloadUrl().
getDownloadURL(ref(storage, 'images/stars.jpg'))
.then((url) => {
// `url` is the download URL for 'images/stars.jpg'
}
You can only do this after the upload fully complete.

How to get download url for a Image from Firebase Storage?

I am trying to upload an image to firebase storage after that I am trying to get the download url of image but it's not working. Below is the code :
const response = await fetch(selectedImage.uri);
const file = await response.blob();
const storageRef = ref(storage, `profile/${currentUser.email}`);
uploadBytes(storageRef, file).then( (snapshot) => {
console.log('uploaded');
getDownloadURL(storage).then( url => console.log(url)); // tried this outside of then block as well
});
the file is uploaded and accessible through console but getDownloadURL throws error :
[Unhandled promise rejection: TypeError: ref._throwIfRoot is not a function. (In 'ref._throwIfRoot('getDownloadURL')', 'ref._throwIfRoot' is undefined)]
Instead of passing storage to getDownloadURL(), pass snapshot.ref.
const response = await fetch(selectedImage.uri);
const file = await response.blob();
const storageRef = ref(storage, `profile/${currentUser.email}`);
uploadBytes(storageRef, file).then( (snapshot) => {
console.log('uploaded');
getDownloadURL(snapshot.ref).then( url => console.log(url));
});

Upload Image to Cloud Storage from Cloud Function Trigger

I'm currently seeking some help with my Cloud Function that is triggered by a Cloud Storage Upload. It checks if the file is a Video, if so we process this Video through ffmpeg to extract a single frame to be used for a Poster Image later.
It all seems to work except my upload of the image back to Cloud Storage doesn't work. At this point where my Cloud Function is it doesn't produce any errors at all, so i have no clue why the upload of the image to Cloud Storage is not working. I would greatly appreciate if anyone with the experience can review my Cloud Function below and provide some insight into why it's not working. Please advice if possible!! Thank you!!!! ^_^
Note: Screenshot of Cloud Function Log is provided below the code snippet
const admin = require('firebase-admin'); // Firebase Admin SDK
const functions = require('firebase-functions'); // Firebase Cloud Functions
const gcs = require('#google-cloud/storage')(); // Cloud Storage Node.js Client
const path = require('path'); // Node.js file and directory utility
const os = require('os'); // Node.js operating system-related utility
const fs = require('fs'); // Node.js file system API
const ffmpeg = require('fluent-ffmpeg');
const ffmpegPath = require('#ffmpeg-installer/ffmpeg').path;
const ffprobePath = require('#ffprobe-installer/ffprobe').path;
// Initialize Firebase Admin
admin.initializeApp(functions.config().firebase);
// Listen for changes in Cloud Storage bucket
exports.storageFunction = functions.storage.object()
.onChange((event) => {
const file = event.data; // The Storage object.
const fileBucket = file.bucket; // The Storage bucket that contains the file.
const filePath = file.name; // File path in the bucket.
const fileName = path.basename(filePath); // Get the file name.
const fileType = file.contentType; // File content type.
if (!fileType.startsWith('video/')) {
return;
}
const bucket = gcs.bucket(fileBucket);
const tempFilePath = path.join(os.tmpdir(), fileName);
const tempFolderPath = os.tmpdir();
// Download video to temp directory
return bucket.file(filePath).download({
destination: tempFilePath
}).then(() => {
console.log('Video downloaded locally to', tempFilePath);
// Generate screenshot from video
ffmpeg(tempFilePath)
.setFfmpegPath(ffmpegPath)
.setFfprobePath(ffprobePath)
.on('filenames', (filenames) => {
console.log(`Will generate ${filenames}`);
})
.on('error', (err) => {
console.log(`An error occurred: ${err.message}`);
})
.on('end', () => {
console.log(`Output image created at ${tempFilePath}`);
const targetTempFileName = `${fileName}.png`;
const targetFilePath = path.join(path.dirname(filePath), targetTempFileName);
console.log(targetTempFileName);
console.log(targetFilePath);
// Uploading the image.
return bucket.upload(tempFilePath, { destination: targetFilePath })
.then(() => {
console.log('Output image uploaded to', filePath);
})
.catch((err) => {
console.log(err.message);
});
})
.screenshots({
count: 1,
folder: tempFolderPath
});
});
});
Cloud Function Log
It looks like you're trying to return a promise from the ffmpeg callback API:
.on('end', () => {
return bucket.upload(tempFilePath, { destination: targetFilePath })
.then(...)
})
I don't know the ffmpeg API, but I'm almost certain that will not cause the function to wait for the upload to complete. Instead, you need to return a promise from directly from your function that resolves only after all the async work is complete.
If the last item of work is inside a callback, and you need to wait for that, you can wrap the entire thing into a new promise and manually resolve it at the right time. In pseudocode:
return new Promise((resolve, reject) => {
// ffmpeg stuff here...
.on('end', () => {
// the last bit of work here...
bucket.upload(...)
.then(() => { resolve() })
})
})
Notice how the resolve method provided by the new promise is being called to indicate when that promise should itself resolve.

How to download entire folder from Firebase Storage?

I want to download an entire folder from Firebase storage. I can download single files using DownloadURL as follows, but it does not work for folders.
var storageRef = firebase.storage().ref();
// Create a reference to the file we want to download
var starsRef = storageRef.child(path);
// Get the download URL
starsRef.getDownloadURL().then(function(url) {
// Insert url into an <img> tag to "download"
ImageUrl = url;
console.log(ImageUrl);
}).catch(function(error) {
switch (error.code) {
case 'storage/object_not_found':
// File doesn't exist
break;
case 'storage/unauthorized':
// User doesn't have permission to access the object
break;
case 'storage/canceled':
// User canceled the upload
break;
case 'storage/unknown':
// Unknown error occurred, inspect the server response
break;
}
});
How to download entire folder from Firebase?
You can use gsutil to download the whole storage bucket
gsutil -m cp -R gs://<bucket_name> .
There is no API in Firebase Storage to download all files in a folder. You will have to download the files one by one, or create a zip file that contains all the files.
As Lahiru's answer shows it can be accomplished with gsutils, but that's a server-side operation - not something you'd run in your client-side application.
Related:
How to get a list of all files in Cloud Storage in a Firebase app?
Command gustil for Windows !!!
gsutil cp -r gs://<bucket_name>.appspot.com/OBJECT_NAME "D:\path"
Use Cloud tools for PowerShell
REF for install windows >> https://cloud.google.com/storage/docs/gsutil_install
You can download the folder by creating a zip file of it.
Here is a sample function:
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
import {
getStorage,
listAll,
ref,
getDownloadURL,
getMetadata,
} from 'firebase/storage';
import { auth } from '../../Firebase';
export const downloadFolderAsZip = async () => {
const jszip = new JSZip();
const storage = getStorage();
const folderRef = ref(
storage,
'images'
);
const folder = await listAll(folderRef);
const promises = folder.items
.map(async (item) => {
const file = await getMetadata(item);
const fileRef = ref(storage, item.fullPath);
const fileBlob = await getDownloadURL(fileRef).then((url) => {
return fetch(url).then((response) => response.blob());
});
jszip.file(file.name, fileBlob);
})
.reduce((acc, curr) => acc.then(() => curr), Promise.resolve());
await promises;
const blob = await jszip.generateAsync({ type: 'blob' });
saveAs(blob, 'download.zip');
};
For a recursive solution that includes subfolders in the zip file, see the following sample. You'll instantiate a jszip object, await promises from a function that zips files and traverses the directories, then save the zip. If the content is a file ("item"), it is zipped into the jszip object. If it is a folder ("prefix"), the function is called again with a new subpath, passing in the same jszip object. For further improvement, you may want to get contents with list and pagination if your contents are too many for listAll, since listAll limits retrievals.
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
import {
getStorage, ref, getBlob, listAll,
} from "firebase/storage";
const addFilesFromDirectoryToZip = async (directoryPath = "", zip) => {
const storage = getStorage();
const directoryContentsRef = ref(
storage,
directoryPath
);
const directoryContents = await listAll(directoryContentsRef);
for (const file of directoryContents.items) {
const fileRef = ref(storage, file.fullPath);
const fileBlob = await getBlob(fileRef)
zip.file(file.fullPath, fileBlob);
}
for (const folder of directoryContents.prefixes) {
await addFilesFromDirectoryToZip(folder.fullPath, zip);
};
};
export const downloadFolderAsZip = async (directoryPath = "") => {
const zip = new JSZip();
await addFilesFromDirectoryToZip(directoryPath, zip);
const blob = await zip.generateAsync({ type: "blob" });
const name = directoryPath.split('/').pop();
saveAs(blob, name);
};

Categories