如何使用Apps Script从Google Drive上传文件到Firebase存储?

4

我需要一些非技术人员能够上传文件到 Firebase 存储,因此我将让他们先上传到 Google Drive,并从存储中镜像。问题是,我无法在不托管服务器的情况下解决这个问题;Google Apps 脚本不能轻松访问 Firebase 存储(尽管它可以访问 Firebase 数据库),我需要一个服务器来使用 Google Drive API。这可以使用 Firebase Cloud Functions 来实现,但我想知道是否有更简单的方法。


你需要自己实现这个功能,因为Drive API中没有提到。我的建议是创建你自己的Web客户端,使其能够接受特定用户的上传/下载请求。你可以参考开始使用存储安全规则了解更多信息。 - ReyAnthonyRenacia
1个回答

7

使用Apps Script可以将图像文件上传到Firebase存储。

需要完成4个关键步骤:

  • 启用“Google Cloud Storage JSON API”
  • 获取Firebase存储的“bucket”名称
  • 在appsscript.json清单文件中添加“https://www.googleapis.com/auth/devstorage.read_write”范围以及所有其他已经需要的范围。
  • 在Firebase存储规则中启用“写入”访问权限

您需要获取OAuth令牌,但不需要OAuth库。

启用“Google Cloud Storage JSON API”

这需要针对将上传文件的Google帐户完成。本解决方案适用于上传Apps Script项目和Firebase存储由同一Google帐户拥有的文件。

  • 进入您的Google Cloud Platform - 从代码编辑器选择“资源”和“Cloud Platform项目” - 单击对话框中的某个内容以进入Cloud Platform。找到“API和服务”部分。单击“启用API和服务” 搜索“JSON” 启用“Google Cloud Storage JSON API”服务。

获取Firebase存储的“bucket”名称

进入您的Firebase存储设置。查找“gs://your-bucket-name.appsspot.com”,这是您的bucket名称。不要包括“gs://”。bucket名称需要在末尾加上“appspot.com”部分。

向appsscript.json清单文件添加“https://www.googleapis.com/auth/devstorage.read_write”范围

从脚本编辑器中选择“文件”和“项目属性”,然后单击“范围”选项卡。将所有现有范围复制出来,并将它们粘贴到某个地方,以便您可以重新获取它们。

从脚本编辑器中选择“查看”和“显示清单文件”。单击appsscript.json文件以打开它。将所有现有范围以及“https://www.googleapis.com/auth/devstorage.read_write”范围添加到清单文件中。

清单文件应如下所示,除了您的时区和范围。

{
  "timeZone": "America/New_York",
  "dependencies": {
  },
  "webapp": {
    "access": "ANYONE_ANONYMOUS",
    "executeAs": "USER_DEPLOYING"
  },
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": [
    "https://mail.google.com/",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/script.container.ui",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/script.scriptapp",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/devstorage.read_write"
  ]
}

在Firebase存储规则中启用“写入”访问权限

allow read, write: if request.auth != null;

Firebase Storage Rules

获取OAuth令牌:

您可以使用以下方法获取OAuth令牌:

ScriptApp.getOAuthToken();

所以,您不需要OAuth库,也不需要任何SDK,也不需要在客户端代码中进行任何操作,也不需要从Firebase服务帐户或Legacy Database Secret获取特殊信息。

代码:

此代码将来自Google Drive的图像文件上传到Firebase Storage

注意! 任何超过5MB的文件可能需要做一些不同的事情。

function uploadToFirebaseStorage(po) {
try{
  var blob,bucketName,bytes,fileID,fileName,folderName,
    oA_Tkn,options,pathAndName,response,result,url;

  /* See
    https://cloud.google.com/storage/docs/uploading-objects?authuser=0
    for REST API information
  */

  /*
    Firebase uses the Google Cloud Storage API

  */

  bucketName = "your-bucket-name.appspot.com";
  folderName = "folder_name";
  fileName = "file_name";

  pathAndName = folderName + "/" + fileName;

  fileID = po.fileId;

  //curl "https://www.googleapis.com/upload/storage/v1/b/[BUCKET_NAME]/o?uploadType=media&name=[OBJECT_NAME]"
  url = 'https://www.googleapis.com/upload/storage/v1/b/' + bucketName + '/o?uploadType=media&name=' + pathAndName;

  blob = DriveApp.getFileById(fileID).getBlob();
  //Logger.log('blob.getContentType(): ' + blob.getContentType())

  bytes = blob.getBytes();
  //Logger.log('bytes: ' + bytes)

  oA_Tkn = ScriptApp.getOAuthToken();
  options = {
    method: "POST",//curl -X POST
    muteHttpExceptions: true,
    contentLength: bytes.length,
    contentType: blob.getContentType(),//curlv-H "Content-Type: [OBJECT_CONTENT_TYPE]"
    payload: bytes,
    headers: {//curl -H "Authorization: Bearer [OAUTH2_TOKEN]"
    Authorization: 'Bearer ' + oA_Tkn
    }
  }

  response = UrlFetchApp.fetch(url, options);

  result = JSON.parse(response.getContentText());
  Logger.log(JSON.stringify(result, null, 2));


  /*
    A successful return object looks like:

{
"kind": "storage#object",
"id": "bucket-name.appspot.com/online_store/file_name/abc123",
"selfLink": "https://www.googleapis.com/storage/v1/b/bucket-name.appspot.com/o/online_store%2FAAA_Test",
"name": "folder_Name/file_name",
"bucket": "bucket-name.appspot.com",
"generation": "abc123",
"metageneration": "1",
"contentType": "image/jpeg",
"timeCreated": "2018-10-24T00:47:33.435Z",
"updated": "2018-10-24T00:47:33.435Z",
"storageClass": "STANDARD",
"timeStorageClassUpdated": "2018-10-24T00:47:33.435Z",
"size": "950012",
"md5Hash": "abc123==",
"mediaLink": "https://www.googleapis.com/download/storage/v1/b/bucket-name.appspot.com/o/some_name%2FAAA_Test?generation=abc123&alt=media",
"crc32c": "kIY6Qg==",
"etag": "nwrfwfn="
}
*/
}catch(e) {
  Logger.log(e.message + "\n\n" + e.stack)

}
}

function testFB_Upload() {
  uploadToFirebaseStorage({fileId:"Put image file ID here"});
}

当代码第一次运行时,如果用户未启用API,则响应中的扩展错误消息提供了一个链接。因此,您可以修改代码以从错误响应中获取Cloud Console链接。该链接直接进入正确的Cloud Console API,因此用户不需要知道如何导航其Cloud Console以查找正确的API。

如何在图像上传后获取下载URL

<head>
    <title>Your Site Name</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Firebase App is always required and must be first -->
    <script src="https://www.gstatic.com/firebasejs/5.5.7/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.5.7/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.5.7/firebase-storage.js"></script>

  <script>
    // Initialize Firebase
    //Open the project - click Project Overview - Click the </> icon
    var config = {
      apiKey: "abc123",//Web API key in project settings
      authDomain: "your_name.firebaseapp.com",
      //databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
      projectId: "myID",//In Project Settings
      storageBucket: "myDomain.appspot.com"
    };
    firebase.initializeApp(config);
  </script>

</head>



window.srchForFile = function(po) {//client side code in a script tag
  //This function is called from a success handler AFTER the file has
  //originally been uploaded
try{
  /*
    po - parameters object - {fileID:'123ABC',folderName:'name_here'}
    po.fileID - the ID of the original file that was uploaded
    po.folderName - the name of the firebase folder to search
  */

  /*
    This code assumes that the firebase SDK has been loaded and that the
    firebase class is available
  */

  var fileID,fileName,imagesRef,spaceRef;

  fileID = po.fileId;
  fileName = "IMG_" + fileID;//The file name to search for which must
    //be exactly the same as the file just uploaded - make sure to use
    //a naming convention that is consistent

  if (!STORAGE_REF) {
    STORAGE_REF = firebase.storage().ref();//firebase SDK must be loaded
  }

  imagesRef = STORAGE_REF.child(po.folderName);
  //console.log('imagesRef: ' + imagesRef);

  spaceRef = imagesRef.child(fileName);// Points to the file name
  //console.log('spaceRef: ' + spaceRef);

  spaceRef.getDownloadURL().then(function(url) {
    //console.log('File available at: ' + url);

    if (!url) {
      url = false;
    }

    nextStepAfterFileSrch(url);//Now run another function
  }).catch(function(error) {//There was an error
     // Handle any errors here
     nextStepAfterFileSrch(false);
   }

  );

  //DO NOT HAVE ANY CODE HERE OR IT WILL RUN BEFORE THE ABOVE CODE IS
  //DONE
} catch(e) {
  showErrMsg(e.message);//client side error handling
}
}

1
谢谢@Alan,我已经挣扎了两天试图弄清楚这个问题。然而,我想在上传过程完成后获取文件的URL,这可能吗? - ilya
1
据我所知,在服务器请求的返回对象中没有提供下载 URL。如果我错了或者它变成可用的,有人可以发表评论或更新答案。我已经更新了答案,并展示了代码,说明我如何在图片上传后获取 Firebase 图片的下载 URL。当文件保存到 Firebase 时,请确保给该文件一个唯一的名称。我将文件保存为原始文件 ID 的名称。然后,从客户端代码中,搜索 Firebase 的文件 ID 并获取下载 URL。 - Alan Wells
你在回答中提到,如果文件大于5MB,可能需要一些不同的东西。为什么会这样,我们该如何解决? - ilya
我不知道。我还没有需要找出如何做到这一点,所以我也没有进行过研究。 - Alan Wells

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接