How to download entire folder from Firebase Storage? - javascript

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);
};

Related

Can't upload image to firebase storage VueJs

I am creating a site for my store and learning Vue js simultaneously. I'm stuck in the part regarding uploading a product image to firebase. I get that storageRef.put is not a function. I'm using Vue js 3 and firebase 9.
uploadImage(e) {
const file = e.target.files[0];
const storage = getStorage();
const storageRef = storageReference(storage, 'products/' + file.name);
storageRef.put(file);
}
This syntax you provide is for firebase version 8.
For version 9
import { getStorage, ref, uploadBytes } from "firebase/storage";
uploadImage(e) {
const file = e.target.files[0];
const storage = getStorage();
// Create a reference to 'mountains.jpg'
const storageRef = ref(storage, 'products/' + file.name);
uploadBytes(storageRef, file).then((snapshot) => {
console.log('Uploaded!');
});
}
For more resources.
https://firebase.google.com/docs/storage/web/upload-files#web-version-9

How to download file directly in node response in react?

How do I download the document I receive in return in react?
Here is the my node.js app. fetchContracts is a function which getting data from mongodb then ganere a excel file by using json2xls npm package.
Its returns as like this:
const xls = json2xls(contracts);
return xls;
If tying to write file fs.writeFileSync(path.join(__dirname, filename), xls, 'binary'); generating successfully xlsx file in the server.
But I need to send the file to the server without writing file. For this, I made some experiments that you can see below.
export const EXPORT_EXCEL: SessionedAsyncControllerType = async (req: SessionedRequest, res: Response) => {
const fileName = 'hello_world.xlsx'
const fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
const xls = await fetchContracts({}, "fileName.xlsx")
const fileData = xls;
res.writeHead(200, {
'Content-Disposition': `attachment; filename="${fileName}"`,
'Content-Type': fileType,
})
const download = Buffer.from(fileData, 'base64')
res.end(download)
}
I getting response like this.
But i don't know how can i download the response file in react?
In react side:
return api.get(`api/excel`).then((response: any) => {
console.log(response);
})
I just log into console. How can i download directly file which coming node response in react.js?
Try this
return api.get(`api/excel`).then((response: any) => {
const outputFilename = `${Date.now()}.xlsx`;
// If you want to download file automatically using link attribute.
const url = URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', outputFilename);
link.click();
})

Uploading an mp3 to Firebase Storage with React Native Expo

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).

amazon s3.upload is taking time

I am trying to upload file to s3, before that I am altering the name of the file. Now I am accepting 2 files from request form-data object, renaming the filename, and uploading the file to s3. And end of the task I need to return the renamed file list which is uploaded successfully.
I am using S3.upload() function. But the problem is, the variable which is assigned as empty array initially, that will contain the renamed file list. But the array is returning empty response. The s3.upload() is taking much time. is there any probable solution where I can store the file name if upload is successful and return those names in response.
Please help me to fix this. The code looks like this,
if (formObject.files.document && formObject.files.document.length > 0) {
const circleCode = formObject.fields.circleCode[0];
let collectedKeysFromAwsResponse = [];
formObject.files.document.forEach(e => {
const extractFileExtension = ".pdf";
if (_.has(FILE_EXTENSIONS_INCLUDED, _.lowerCase(extractFileExtension))) {
console.log(e);
//change the filename
const originalFileNameCleaned = "cleaning name logic";
const _id = mongoose.Types.ObjectId();
const s3FileName = "s3-filename-convension;
console.log(e.path, "", s3FileName);
const awsResponse = new File().uploadFileOnS3(e.path, s3FileName);
if(e.hasOwnProperty('ETag')) {
collectedKeysFromAwsResponse.push(awsResponse.key.split("/")[1])
}
}
});
};
use await s3.upload(params).promise(); is the solution.
Use the latest code - which is AWS SDK for JavaScript V3. Here is the code you should be using
// Import required AWS SDK clients and commands for Node.js.
import { PutObjectCommand } from "#aws-sdk/client-s3";
import { s3Client } from "./libs/s3Client.js"; // Helper function that creates Amazon S3 service client module.
import {path} from "path";
import {fs} from "fs";
const file = "OBJECT_PATH_AND_NAME"; // Path to and name of object. For example '../myFiles/index.js'.
const fileStream = fs.createReadStream(file);
// Set the parameters
export const uploadParams = {
Bucket: "BUCKET_NAME",
// Add the required 'Key' parameter using the 'path' module.
Key: path.basename(file),
// Add the required 'Body' parameter
Body: fileStream,
};
// Upload file to specified bucket.
export const run = async () => {
try {
const data = await s3Client.send(new PutObjectCommand(uploadParams));
console.log("Success", data);
return data; // For unit tests.
} catch (err) {
console.log("Error", err);
}
};
run();
More details can be found in the AWS JavaScript V3 DEV Guide.

App exits automatically once it saves a .xlsx file into the phone storage

I'm able to save .xlsx file into the phone storage but I've two issues.
App exits automatically once it saves a .xlsx file into the phone storage.
.xlsx file is saving in 'DCIM' folder, but I need to save in 'Download' folder.
handleDownload = async () => {
const data = this.handleDownloadAbleLogs();
if (!data.length)
return ToastAndroid.showWithGravity(
"Sorry, no logs available to download.",
ToastAndroid.LONG,
ToastAndroid.CENTER
);
const ws = XLSX.utils.json_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Logs");
const wbout = XLSX.write(wb, {
type: "base64",
bookType: "xlsx",
});
const uri = `${FileSystem.cacheDirectory}logs.xlsx`;
try {
await FileSystem.writeAsStringAsync(uri, wbout, {
encoding: FileSystem.EncodingType.Base64,
});
console.log(uri);
this.saveXLSXFile(uri);
alert("Logs file has been saved in DCIM folder, do check there.");
} catch (e) {
console.error(e);
}};
saveXLSXFile = async (fileUri) => {
const { status } = await MediaLibrary.requestPermissionsAsync();
if (status === "granted") {
const asset = await MediaLibrary.createAssetAsync(fileUri);
await MediaLibrary.createAlbumAsync("Download", asset, false);
} else alert("We need you permission to download this file.");
};
Its quite simple change the path string to '../', in most android phone Pictures and Download folders are at the same level in hierarchy so doing '../' will take one step back and save in the Download folder
await MediaLibrary.createAlbumAsync("../Download", asset, false);

Categories