如何从云存储 [Firebase 托管] 下载文件
How to download file from Cloud Storage [Firebase Hosting]
我想使用 Firebase 托管制作一个网络应用程序。
- 使用云文本到语音制作音频文件API
- 将该音频文件上传到云存储
- 将该音频文件从云存储下载到网络浏览器
我通过了第 1 步和第 2 步,但在第 3 步时遇到了问题。
我关注了这个教程。
https://firebase.google.com/docs/storage/web/download-files
我部署了我的 Firebase 项目并测试了我的应用程序。我可以将音频文件上传到 Cloud Storage,但无法下载。我查看了浏览器的控制台,但找不到任何错误消息。浏览器的控制台中没有消息。
你能给我什么建议吗?提前谢谢你。
这是我的main.js
'use strict';
// Saves a new message on the Cloud Firestore.
function saveMessage() {
// Add a new message entry to the Firebase database.
return firebase.firestore().collection('messages').add({
text: messageInputElement.value,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
}).catch(function(error) {
console.error('Error writing new message to Firebase Database', error);
});
}
// Checks that the Firebase SDK has been correctly setup and configured.
function checkSetup() {
if (!window.firebase || !(firebase.app instanceof Function) || !firebase.app().options) {
window.alert('You have not configured and imported the Firebase SDK. ' +
'Make sure you go through the codelab setup instructions and make ' +
'sure you are running the codelab using `firebase serve`');
}
}
// Checks that Firebase has been imported.
checkSetup();
// Shortcuts to DOM Elements.
var messageInputElement = document.getElementById('text');
var submitButtonElement = document.getElementById('download');
// Saves message on form submit.
submitButtonElement.addEventListener('click', saveMessage);
// Create a reference from a Google Cloud Storage URI
var storage = firebase.storage();
var gsReference = storage.refFromURL('gs://advan********8.appspot.com/audio/sub.mp3')
gsReference.getDownloadURL().then(function(url) {
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
}).catch(function(error) {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/object-not-found':
console.log('storage/object-not-found')
break;
case 'storage/unauthorized':
console.log('storage/unauthorized')
break;
case 'storage/canceled':
console.log('storage/canceled')
break;
case 'storage/unknown':
console.log('storage/unknown')
break;
}
});
这是index.js(云函数)
const functions = require('firebase-functions');
var admin = require("firebase-admin");
admin.initializeApp();
const textToSpeech = require('@google-cloud/text-to-speech');
exports.myFunction = functions.firestore
.document('messages/{id}')
.onCreate((change, context) => {
const client = new textToSpeech.TextToSpeechClient();
async function quickStart() {
// The text to synthesize
const text = 'Hello world';
// Construct the request
const request = {
input: {text: text},
// Select the language and SSML voice gender (optional)
voice: {languageCode: 'en-US', ssmlGender: 'NEUTRAL'},
// select the type of audio encoding
audioConfig: {audioEncoding: 'MP3'},
};
var bucket = admin.storage().bucket('adva********.appspot.com');
var file = bucket.file('audio/sub.mp3')
// Create the file metadata
var metadata = {
contentType: 'audio/mpeg'
};
// Performs the text-to-speech request
const [response] = await client.synthesizeSpeech(request);
return await file.save(response.audioContent, metadata)
.then(() => {
console.log("File written to Firebase Storage.")
return;
})
.catch((error) => {
console.error(error);
});
}
quickStart();
});
这是index.html
<!--./advance/index.html-->
<!doctype html>
<html lang="ja">
<head>
<meta name="robots" content="noindex">
<title>音読アプリ アドバンス</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://fonts.googleapis.com/css?family=M+PLUS+Rounded+1c&display=swap" rel="stylesheet">
<style>
#text {width: 100%; height: 300px; font-family: 'M PLUS Rounded 1c', sans-serif; font-size: 22px;}
#download {font-family: 'M PLUS Rounded 1c', sans-serif; font-size: 28px;}
</style>
</head>
<body>
<textarea id="text" class="form-control" name="text" placeholder="ここに英文を入力してください。" maxlength="3000" minlength="1"></textarea>
<br>
<div style="text-align:center">
<input id="download" class="btn btn-primary" type="submit" value="音声をダウンロード">
</div>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<!-- Import and configure the Firebase SDK -->
<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->
<!-- If you do not want to serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->
<script src="/__/firebase/7.14.3/firebase-app.js"></script>
<script src="/__/firebase/7.14.3/firebase-auth.js"></script>
<script src="/__/firebase/7.14.3/firebase-storage.js"></script>
<script src="/__/firebase/7.14.3/firebase-messaging.js"></script>
<script src="/__/firebase/7.14.3/firebase-firestore.js"></script>
<script src="/__/firebase/7.14.3/firebase-performance.js"></script>
<script src="/__/firebase/7.14.3/firebase-functions.js"></script>
<script src="/__/firebase/init.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>
浏览器的开发者工具的网络选项卡
问题是您可能会在云函数创建文件之前尝试下载,因为您 运行 云函数作为事件触发器,每次创建文档时都会自动运行,但与此同时,您正试图在前端下载该文件。缺乏同步性会导致您看到这种奇怪的行为。
为了解决这个问题,您应该做几件事:
- 将您的云函数转换为 http 触发函数而不是事件触发函数。
这样一来,您的函数就可以在创建文档之后和您尝试下载它之前被前端调用,它可能看起来像这样:
exports.myFunction = (req, res) => {
//you can get the id of the document sent on the request here
const id=req.body;
...
};
此外,您可能需要查看此 documentation 以了解有关此类触发器的更多详细信息
- 创建一个下载函数并将所有下载代码添加到其中,并为您在前端的操作添加同步性。
这样您的代码将按正确的顺序执行,您 main.js 将如下所示:
// Saves a new message on the Cloud Firestore.
function saveMessage() {
// Add a new message entry to the Firebase database.
firebase.firestore().collection('messages').add({
text: messageInputElement.value,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
})
.then(function(docRef){
var obj = {
method: 'POST',
body: docRef.id
};
//calls function that adds to storage
fetch("YOUR_FUNTION_URL_HERE", obj).then({
//actually downloads
download();
}).catch(error) {
console.error('Failed to call cloud function', error);
});
}).catch(function(error) {
console.error('Error writing new message to Firebase Database', error);
});
}
function download(){
var storage = firebase.storage();
var gsReference = storage.refFromURL('gs://advan********8.appspot.com/audio/sub.mp3')
gsReference.getDownloadURL().then(function(url) {
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
}).catch(function(error) {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/object-not-found':
console.log('storage/object-not-found')
break;
case 'storage/unauthorized':
console.log('storage/unauthorized')
break;
case 'storage/canceled':
console.log('storage/canceled')
break;
case 'storage/unknown':
console.log('storage/unknown')
break;
}
});
}
// Checks that Firebase has been imported.
checkSetup();
// Shortcuts to DOM Elements.
var messageInputElement = document.getElementById('text');
var submitButtonElement = document.getElementById('download');
// Saves message on form submit.
submitButtonElement.addEventListener('click', saveMessage);
注意:这一切都未经测试,但它将是您开始对代码进行必要更改的良好起点。
我希望有人仍然觉得这很有用。一个 hack around 是创建一个 express server/lambda/cloud 函数来下载文件并通过 download 方法将其发送回浏览器。检查下面的代码
const express = require("express");
const app = express();
var fs = require("fs");
const request = require("request");
app.get("/", (req, res) => {
const { filename, fileUrl } = req.body;
const file = fs.createWriteStream("filename");
request.get(fileUrl).on("response", function (response) {
var pipe = response.pipe(file);
pipe.on("finish", function () {
res.download(filename, function (err) {
if (err) {
console.log(err); // Check error if you want
}
fs.unlink(yourFilePath, function () {
console.log("File was deleted"); // Callback
});
});
});
});
});
app.listen(3000, () => console.log("Server ready"));
我想使用 Firebase 托管制作一个网络应用程序。
- 使用云文本到语音制作音频文件API
- 将该音频文件上传到云存储
- 将该音频文件从云存储下载到网络浏览器
我通过了第 1 步和第 2 步,但在第 3 步时遇到了问题。 我关注了这个教程。
https://firebase.google.com/docs/storage/web/download-files
我部署了我的 Firebase 项目并测试了我的应用程序。我可以将音频文件上传到 Cloud Storage,但无法下载。我查看了浏览器的控制台,但找不到任何错误消息。浏览器的控制台中没有消息。
你能给我什么建议吗?提前谢谢你。
这是我的main.js
'use strict';
// Saves a new message on the Cloud Firestore.
function saveMessage() {
// Add a new message entry to the Firebase database.
return firebase.firestore().collection('messages').add({
text: messageInputElement.value,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
}).catch(function(error) {
console.error('Error writing new message to Firebase Database', error);
});
}
// Checks that the Firebase SDK has been correctly setup and configured.
function checkSetup() {
if (!window.firebase || !(firebase.app instanceof Function) || !firebase.app().options) {
window.alert('You have not configured and imported the Firebase SDK. ' +
'Make sure you go through the codelab setup instructions and make ' +
'sure you are running the codelab using `firebase serve`');
}
}
// Checks that Firebase has been imported.
checkSetup();
// Shortcuts to DOM Elements.
var messageInputElement = document.getElementById('text');
var submitButtonElement = document.getElementById('download');
// Saves message on form submit.
submitButtonElement.addEventListener('click', saveMessage);
// Create a reference from a Google Cloud Storage URI
var storage = firebase.storage();
var gsReference = storage.refFromURL('gs://advan********8.appspot.com/audio/sub.mp3')
gsReference.getDownloadURL().then(function(url) {
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
}).catch(function(error) {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/object-not-found':
console.log('storage/object-not-found')
break;
case 'storage/unauthorized':
console.log('storage/unauthorized')
break;
case 'storage/canceled':
console.log('storage/canceled')
break;
case 'storage/unknown':
console.log('storage/unknown')
break;
}
});
这是index.js(云函数)
const functions = require('firebase-functions');
var admin = require("firebase-admin");
admin.initializeApp();
const textToSpeech = require('@google-cloud/text-to-speech');
exports.myFunction = functions.firestore
.document('messages/{id}')
.onCreate((change, context) => {
const client = new textToSpeech.TextToSpeechClient();
async function quickStart() {
// The text to synthesize
const text = 'Hello world';
// Construct the request
const request = {
input: {text: text},
// Select the language and SSML voice gender (optional)
voice: {languageCode: 'en-US', ssmlGender: 'NEUTRAL'},
// select the type of audio encoding
audioConfig: {audioEncoding: 'MP3'},
};
var bucket = admin.storage().bucket('adva********.appspot.com');
var file = bucket.file('audio/sub.mp3')
// Create the file metadata
var metadata = {
contentType: 'audio/mpeg'
};
// Performs the text-to-speech request
const [response] = await client.synthesizeSpeech(request);
return await file.save(response.audioContent, metadata)
.then(() => {
console.log("File written to Firebase Storage.")
return;
})
.catch((error) => {
console.error(error);
});
}
quickStart();
});
这是index.html
<!--./advance/index.html-->
<!doctype html>
<html lang="ja">
<head>
<meta name="robots" content="noindex">
<title>音読アプリ アドバンス</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://fonts.googleapis.com/css?family=M+PLUS+Rounded+1c&display=swap" rel="stylesheet">
<style>
#text {width: 100%; height: 300px; font-family: 'M PLUS Rounded 1c', sans-serif; font-size: 22px;}
#download {font-family: 'M PLUS Rounded 1c', sans-serif; font-size: 28px;}
</style>
</head>
<body>
<textarea id="text" class="form-control" name="text" placeholder="ここに英文を入力してください。" maxlength="3000" minlength="1"></textarea>
<br>
<div style="text-align:center">
<input id="download" class="btn btn-primary" type="submit" value="音声をダウンロード">
</div>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<!-- Import and configure the Firebase SDK -->
<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->
<!-- If you do not want to serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->
<script src="/__/firebase/7.14.3/firebase-app.js"></script>
<script src="/__/firebase/7.14.3/firebase-auth.js"></script>
<script src="/__/firebase/7.14.3/firebase-storage.js"></script>
<script src="/__/firebase/7.14.3/firebase-messaging.js"></script>
<script src="/__/firebase/7.14.3/firebase-firestore.js"></script>
<script src="/__/firebase/7.14.3/firebase-performance.js"></script>
<script src="/__/firebase/7.14.3/firebase-functions.js"></script>
<script src="/__/firebase/init.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>
浏览器的开发者工具的网络选项卡
问题是您可能会在云函数创建文件之前尝试下载,因为您 运行 云函数作为事件触发器,每次创建文档时都会自动运行,但与此同时,您正试图在前端下载该文件。缺乏同步性会导致您看到这种奇怪的行为。
为了解决这个问题,您应该做几件事:
- 将您的云函数转换为 http 触发函数而不是事件触发函数。
这样一来,您的函数就可以在创建文档之后和您尝试下载它之前被前端调用,它可能看起来像这样:
exports.myFunction = (req, res) => {
//you can get the id of the document sent on the request here
const id=req.body;
...
};
此外,您可能需要查看此 documentation 以了解有关此类触发器的更多详细信息
- 创建一个下载函数并将所有下载代码添加到其中,并为您在前端的操作添加同步性。
这样您的代码将按正确的顺序执行,您 main.js 将如下所示:
// Saves a new message on the Cloud Firestore.
function saveMessage() {
// Add a new message entry to the Firebase database.
firebase.firestore().collection('messages').add({
text: messageInputElement.value,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
})
.then(function(docRef){
var obj = {
method: 'POST',
body: docRef.id
};
//calls function that adds to storage
fetch("YOUR_FUNTION_URL_HERE", obj).then({
//actually downloads
download();
}).catch(error) {
console.error('Failed to call cloud function', error);
});
}).catch(function(error) {
console.error('Error writing new message to Firebase Database', error);
});
}
function download(){
var storage = firebase.storage();
var gsReference = storage.refFromURL('gs://advan********8.appspot.com/audio/sub.mp3')
gsReference.getDownloadURL().then(function(url) {
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
}).catch(function(error) {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/object-not-found':
console.log('storage/object-not-found')
break;
case 'storage/unauthorized':
console.log('storage/unauthorized')
break;
case 'storage/canceled':
console.log('storage/canceled')
break;
case 'storage/unknown':
console.log('storage/unknown')
break;
}
});
}
// Checks that Firebase has been imported.
checkSetup();
// Shortcuts to DOM Elements.
var messageInputElement = document.getElementById('text');
var submitButtonElement = document.getElementById('download');
// Saves message on form submit.
submitButtonElement.addEventListener('click', saveMessage);
注意:这一切都未经测试,但它将是您开始对代码进行必要更改的良好起点。
我希望有人仍然觉得这很有用。一个 hack around 是创建一个 express server/lambda/cloud 函数来下载文件并通过 download 方法将其发送回浏览器。检查下面的代码
const express = require("express");
const app = express();
var fs = require("fs");
const request = require("request");
app.get("/", (req, res) => {
const { filename, fileUrl } = req.body;
const file = fs.createWriteStream("filename");
request.get(fileUrl).on("response", function (response) {
var pipe = response.pipe(file);
pipe.on("finish", function () {
res.download(filename, function (err) {
if (err) {
console.log(err); // Check error if you want
}
fs.unlink(yourFilePath, function () {
console.log("File was deleted"); // Callback
});
});
});
});
});
app.listen(3000, () => console.log("Server ready"));