无法从 nodejs 上传到 google 云或 firebase 存储桶

Can't upload to google cloud or firebase storage bucket from nodejs

我一直在尝试将一些图片上传到我的 firebase 存储桶中。 我已经关注了这个官方文档:-

https://firebase.google.com/docs/storage/admin/start

https://googlecloudplatform.github.io/google-cloud-node/#/docs/storage/latest/storage/bucket

我能够看到存储桶中已经存在的文件(这些文件是使用 android 应用程序存储在那里的)

但我无法从 nodejs 的 admin.storage 模块上传本地文件。

这是代码:

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://myprojectid.firebaseio.com/" , 
  storageBucket : "myprojectid.appspot.com/"
});

bucket = admin.storage().bucket() ; 
bucket.upload('./mylocalfile.jpg' , (err , file , response)=>{
console.log(err) ; //always gives me err
console.log(file) ; //gives undefined
}) ; 

但是 bucket.getFiles().then(objects=>console.log(objects)) ; 工作完美并打印出根目录中的文件。

如何解决这个问题?

这是控制台记录 err 时打印的 error :

{ ApiError: Not Found
    at Object.parseHttpRespMessage (C:\Users\Natesh\Desktop\AttentionPlease_backend\functions\node_modules\firebase-admin\node_modules\@google-cloud\common\src\util.js:156:33)
    at Object.handleResp (C:\Users\Natesh\Desktop\AttentionPlease_backend\functions\node_modules\firebase-admin\node_modules\@google-cloud\common\src\util.js:131:18)
    at C:\Users\Natesh\Desktop\AttentionPlease_backend\functions\node_modules\firebase-admin\node_modules\@google-cloud\common\src\util.js:465:12
    at Request.onResponse [as _callback] (C:\Users\Natesh\Desktop\AttentionPlease_backend\functions\node_modules\firebase-admin\node_modules\retry-request\index.js:179:7)
    at Request.self.callback (C:\Users\Natesh\Desktop\AttentionPlease_backend\functions\node_modules\firebase-admin\node_modules\request\request.js:186:22)
    at emitTwo (events.js:106:13)
    at Request.emit (events.js:191:7)
    at Request.<anonymous> (C:\Users\Natesh\Desktop\AttentionPlease_backend\functions\node_modules\firebase-admin\node_modules\request\request.js:1163:10)
    at emitOne (events.js:96:13)
    at Request.emit (events.js:188:7)
  code: 404,
  errors: [],
  response:
   IncomingMessage {
     _readableState:
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [Object],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: true,
        endEmitted: true,
        reading: false,
        sync: true,
        needReadable: false,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: false,
     domain:
      Domain {
        domain: null,
        _events: [Object],
        _eventsCount: 1,
        _maxListeners: undefined,
        members: [] },
     _events:
      { end: [Object],
        close: [Object],
        data: [Function],
        error: [Function] },
     _eventsCount: 4,
     _maxListeners: undefined,
     socket:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: null,
        npnProtocol: false,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 9,
        connecting: false,
        _hadError: false,
        _handle: [Object],
        _parent: null,
        _host: 'www.googleapis.com',
        _readableState: [Object],
        readable: true,
        domain: [Object],
        _maxListeners: undefined,
        _writableState: [Object],
        writable: true,
        allowHalfOpen: false,
        destroyed: false,
        _bytesDispatched: 4047,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: [Object],
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: null,
        read: [Function],
        _consuming: true,
        _idleTimeout: -1,
        _idleNext: null,
        _idlePrev: null,
        _idleStart: 1307656 },
     connection:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: null,
        npnProtocol: false,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 9,
        connecting: false,
        _hadError: false,
        _handle: [Object],
        _parent: null,
        _host: 'www.googleapis.com',
        _readableState: [Object],
        readable: true,
        domain: [Object],
        _maxListeners: undefined,
        _writableState: [Object],
        writable: true,
        allowHalfOpen: false,
        destroyed: false,
        _bytesDispatched: 4047,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: [Object],
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: null,
        read: [Function],
        _consuming: true,
        _idleTimeout: -1,
        _idleNext: null,
        _idlePrev: null,
        _idleStart: 1307656 },
     httpVersionMajor: 1,
     httpVersionMinor: 1,
     httpVersion: '1.1',
     complete: true,
     headers:
      { 'x-guploader-uploadid': 'AEnB2UqADzuHb4O7UjdBAui1cDWeLNO4s0YuT2krCPoYIHaUrYPjXRH8rBU0mcSi9n7sie11PhALTN2vOKkhykW0apqTbrNB9Q',
        vary: 'Origin, X-Origin',
        'content-type': 'text/html; charset=UTF-8',
        'content-length': '9',
        date: 'Sat, 17 Mar 2018 13:24:19 GMT',
        server: 'UploadServer',
        'alt-svc': 'hq=":443"; ma=2592000; quic=51303431; quic=51303339; quic=51303335,quic=":443"; ma=2592000; v="41,39,35"' },
     rawHeaders:
      [ 'X-GUploader-UploadID',
        'AEnB2UqADzuHb4O7UjdBAui1cDWeLNO4s0YuT2krCPoYIHaUrYPjXRH8rBU0mcSi9n7sie11PhALTN2vOKkhykW0apqTbrNB9Q',
        'Vary',
        'Origin',
        'Vary',
        'X-Origin',
        'Content-Type',
        'text/html; charset=UTF-8',
        'Content-Length',
        '9',
        'Date',
        'Sat, 17 Mar 2018 13:24:19 GMT',
        'Server',
        'UploadServer',
        'Alt-Svc',
        'hq=":443"; ma=2592000; quic=51303431; quic=51303339; quic=51303335,quic=":443"; ma=2592000; v="41,39,35"' ],
     trailers: {},
     rawTrailers: [],
     upgrade: false,
     url: '',
     method: null,
     statusCode: 404,
     statusMessage: 'Not Found',
     client:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: null,
        npnProtocol: false,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 9,
        connecting: false,
        _hadError: false,
        _handle: [Object],
        _parent: null,
        _host: 'www.googleapis.com',
        _readableState: [Object],
        readable: true,
        domain: [Object],
        _maxListeners: undefined,
        _writableState: [Object],
        writable: true,
        allowHalfOpen: false,
        destroyed: false,
        _bytesDispatched: 4047,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: [Object],
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: null,
        read: [Function],
        _consuming: true,
        _idleTimeout: -1,
        _idleNext: null,
        _idlePrev: null,
        _idleStart: 1307656 },
     _consuming: true,
     _dumped: false,
     req:
      ClientRequest {
        domain: [Object],
        _events: [Object],
        _eventsCount: 6,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: false,
        upgrading: false,
        chunkedEncoding: true,
        shouldKeepAlive: true,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedHeader: [Object],
        _contentLength: null,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Object],
        connection: [Object],
        _header: 'POST /upload/storage/v1/b/myprojectid.appspot.com//o?uploadType=multipart&name=profile HTTP/1.1\r\nUser-Agent: gcloud-node-storage/1.4.0\r\nx-goog-api-client: gl-node/6.11.5 gccl/1.4.0\r\nAuthorization: Bearer ya29.c.<PLACEHOLDER_FOR_SOME_LONG_AUTH_ID>\r\nhost: www.googleapis.com\r\naccept-encoding: gzip, deflate\r\ntransfer-encoding: chunked\r\ncontent-type: multipart/related; boundary=6e012a90-2cb1-4c46-9de5-bb75cb5949ea\r\nConnection: keep-alive\r\n\r\n',
        _headers: [Object],
        _headerNames: [Object],
        _onPendingData: null,
        agent: [Object],
        socketPath: undefined,
        timeout: undefined,
        method: 'POST',
        path: '/upload/storage/v1/b/myprojectid.appspot.com//o?uploadType=multipart&name=profile',
        _ended: true,
        parser: null,
        timeoutCb: null,
        res: [Circular] },
     request:
      Request {
        domain: [Object],
        _events: [Object],
        _eventsCount: 5,
        _maxListeners: undefined,
        timeout: 60000,
        gzip: true,
        forever: true,
        pool: [Object],
        method: 'POST',
        uri: [Object],
        headers: [Object],
        callback: [Function],
        readable: true,
        writable: true,
        explicitMethod: true,
        _qs: [Object],
        _auth: [Object],
        _oauth: [Object],
        _multipart: [Object],
        _redirect: [Object],
        _tunnel: [Object],
        setHeader: [Function],
        hasHeader: [Function],
        getHeader: [Function],
        removeHeader: [Function],
        localAddress: undefined,
        dests: [],
        __isRequestRequest: true,
        _callback: [Function: onResponse],
        proxy: null,
        tunnel: true,
        setHost: true,
        originalCookieHeader: undefined,
        _disableCookies: true,
        _jar: undefined,
        port: 443,
        host: 'www.googleapis.com',
        url: [Object],
        path: '/upload/storage/v1/b/myprojectid.appspot.com//o?uploadType=multipart&name=profile',
        httpModule: [Object],
        agentClass: [Object],
        agentOptions: [Object],
        agent: [Object],
        src: [Object],
        _started: true,
        href: 'https://www.googleapis.com/upload/storage/v1/b/myprojectid.appspot.com//o?uploadType=multipart&name=profile',
        req: [Object],
        ntick: true,
        response: [Circular],
        originalHost: 'www.googleapis.com',
        originalHostHeaderName: 'host',
        responseContent: [Circular],
        _destdata: true,
        _ended: true,
        _callbackCalled: true },
     toJSON: [Function: responseToJSON],
     caseless: Caseless { dict: [Object] },
     read: [Function],
     body: 'Not Found' },
  message: 'Not Found' }

不幸的是,'upload' 函数只接收本地文件或 url 的路径,因此在通过云函数实现上传文件 REST api 时会造成一些复杂性。 我猜这不应该是将文件上传到云功能服务器并将本地文件上传到云存储的正确方法。

这是我使用 multer 从请求中获取文件,然后将其转换为数据并使用保存功能上传到 firebase (gcloud) 存储的上传端点所完成的。

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require('express');
const multer  = require('multer');
const app = express();
const fileUpload = multer();

admin.initializeApp(.......);

/*
 * Upload File
 **/
app.post('/', fileUpload.single('file'), functions.https.onRequest((req, res) => {
  const file = req.file;
  const bucket = admin.storage().bucket();
  const name = file.originalname;
  
  const bucketFile = bucket.file(name);

  bucketFile
    .save(new Buffer(file.buffer))
    .then(() => {
      res.status(200).json({
        status: 'success',
        data: Object.assign({}, bucketFile.metadata, {
          downloadURL: `https://storage.googleapis.com/${bucket.name}/${name}`,
        })
      });
    })
    .catch(err => {
      res.status(500).json({
        status: 'error',
        errors: err,
      });
    });
}));

这是我的 POSTMAN 测试屏幕。

如果您需要进一步的帮助,请告诉我。