【问题标题】:Multer Memory Storage to @google-cloud/storage doesn't complete将内存存储到 @google-cloud/storage 未完成
【发布时间】:2021-01-31 21:11:10
【问题描述】:

我有一条 Express 路由,可以连接到我的 google-storage 存储桶并上传一张图片。我试图贴近 Google 的代码示例,但从 multer 缓冲区直接转到存储桶。

该过程似乎已完成,但未返回 en err。但是,该文件以 0B 大小出现在存储桶中。所以看起来元数据到达但图像缓冲区没有......??

谁能看出我做错了什么?

如果可能,我希望在没有其他 NPM 帮助包(如 multer-cloud-storage 和所有其他变体)的帮助下使其工作。

const Multer = require("multer");
const multer = Multer({
    storage: Multer.MemoryStorage,
    fileSize: 5 * 1024 * 1024
  });
const {Storage} = require('@google-cloud/storage');
const storage = new Storage({
    projectId: 'project-name',
    keyFile: '../config/project-name.json'
    });

const bucket = storage.bucket('bucket-name');

app.post('/upload', multer.single('file'), function (req, res) {    

    const blob = bucket.file(req.file.originalname);
    const blobStream = blob.createWriteStream({
        metadata: {
            contentType: req.file.mimetype
        },
        resumable: false
    });
    blobStream.on('error', err => {
        next(err);
        console.log(err);
        return;
    });
    blobStream.on('finish', () => {
        blob.makePublic().then(() => {
            res.status(200).send(`Success!`);
        })
    })
    blobStream.end();
})

【问题讨论】:

  • 我在您的代码中没有看到实际写入 blobStream 的任何地方。
  • 认为 blob.createWriteStream() 继承了req.file.buffer 对象,不是吗?这里描述的方式表明它是通过管道输入的。blog.datawheel.us/…
  • 没有继承任何东西。您必须明确说明如何处理节点流。
  • 谢谢@DougStevenson。我显然超出了我的深度。如何将 multer memorystore 缓冲区通过管道传输到 createWriteStream() 中?

标签: node.js express google-cloud-storage


【解决方案1】:

Node 的原生 stream 对我不起作用。所以,streamifier 就是这样。

一个快速路由,将上传的图像从 Multer MemoryStorage 管道到 GCS 存储桶,绕过持久化到磁盘。

鉴于这是 Google 推荐的模式,我对他们的代码示例不完整以及 SO 上的弱答案感到失望。

    // npm streamifier
    const streamifier = require('streamifier');
    // npm multer
    const Multer = require("multer");
    const multer = Multer({
        storage: Multer.MemoryStorage,
        fileSize: 5 * 1024 * 1024
      });
    // Google Cloud Storage GCS
    const {Storage} = require('@google-cloud/storage');
    const storage = new Storage({
        projectId: 'my-project',
        keyFile: 'my-project.json'
        });
    const bucket = storage.bucket('my-bucket');

    // Express Route Hendler
    app.post('/upload', multer.single('file'), function (req, res) {
        // grab original file name out of multer obj
        const blob = bucket.file(req.file.originalname);
        // create the GCS stream handler
        const blobStream = blob.createWriteStream()
        // Yuck...
        return new Promise((resolve, reject) => {
                streamifier.createReadStream(req.file.buffer)
                    .on('error', (err) => {
                        return reject(err);
                    })
                    .pipe(blobStream)
                    .on('finish', (resp) => {
                       res.send('done') 
                    });
            })
    }); //end route

【讨论】:

    【解决方案2】:

    您正在定义和打开流,但没有向其写入任何内容。

    如果您不想要 multer-cloud-storage 的小开销,我建议您查看 src/index.ts 文件。 _handleFile 函数处理所有的写入任务。魔法发生在

    file.stream.pipe(blobStream)
      .on('error', (err) => cb(err))
      .on('finish', (file) => {
        const name = blob.metadata.name;
        const filename = name.substr(name.lastIndexOf('/')+1);
        cb(null, {
            bucket: blob.metadata.bucket,
            destination: this.blobFile.destination,
            filename,
            path: `${this.blobFile.destination}${filename}`,
            contentType: blob.metadata.contentType,
            size: blob.metadata.size,
            uri: `gs://${blob.metadata.bucket}/${this.blobFile.destination}${filename}`,
            linkUrl: `https://storage.cloud.google.com/${blob.metadata.bucket}/${this.blobFile.destination}${filename}`,
            selfLink: blob.metadata.selfLink,
                            })
    });
    

    【讨论】:

      【解决方案3】:

      我使用类似的方法在没有第三方库的情况下上传到 GCS。但是,过了一段时间,a 注意到节点使用了大量内存,而这些内存没有被垃圾收集。使用 Multer.MemoryStorage 发送 20mb 文件会导致 +20MB 分配给我的节点进程等等。

      为了解决这个问题,我制作了自己的 Multer 存储引擎(基于 3rd 方包),将接收到的文件直接传输到 GSC 流。

      最终代码为:

      import { Request } from "express";
      import { v4 as uuid } from "uuid";
      import { Bucket, Storage } from "@google-cloud/storage";
      import multer from "multer";
      
      export default class MulterGoogleCloudStorage implements multer.StorageEngine {
        private gcsBucket: Bucket;
      
        private gcsStorage: Storage;
      
        private blobFile = {
          filename: ""
        };
      
        private setBlobFile(req: Request, file: Express.Multer.File) {
          const ext = file.mimetype.split("/")[1].split(";")[0];
          const filename = `${uuid()}_${file.originalname || `.${ext}`}`;
      
          this.blobFile.filename = filename
            .replace(/^\.+/g, "")
            .replace(/^\/+/g, "")
            .replace(/\r|\n| /g, "_");
      
          return true;
        }
      
        constructor() {
          this.gcsStorage = new Storage({
            projectId: process.env.GCLOUD_PROJECT!,
            keyFilename: process.env.GCLOUD_KEYFILE!
          });
      
          this.gcsBucket = this.gcsStorage.bucket(process.env.GCLOUD_BUCKET!);
        }
      
        _handleFile = (
          req: Request,
          file: Express.Multer.File,
          cb: (error: Error | null, info?: Partial<Express.Multer.File>) => void
        ): void => {
          if (this.setBlobFile(req, file)) {
            const blobName = this.blobFile.filename;
            const blob = this.gcsBucket.file(blobName);
      
            const blobStream = blob.createWriteStream();
            file.stream
              .pipe(blobStream)
              .on("error", err => cb(err))
              .on("finish", () => {
                const { name } = blob.metadata;
                cb(null, {
                  filename: name,
                  size: blob.metadata.size
                });
              });
          }
        };
      
        _removeFile = (req: Request, file: Express.Multer.File): void => {
          if (this.setBlobFile(req, file)) {
            const blobName = this.blobFile.filename;
            const blob = this.gcsBucket.file(blobName);
            blob.delete();
          }
        };
      }
      
      export function storageEngine(): MulterGoogleCloudStorage {
        return new MulterGoogleCloudStorage();
      }
      
      

      用法:

      import multer from "multer";
      import MulterGoogleCloudStorage from "../helpers/MulterGoogleCloudStorage";
      
      const upload = multer({
        storage: new MulterGoogleCloudStorage()
      });
      
      app.post("/upload", upload.array("medias"), mycontroller.store);
      
      ...
      
      

      【讨论】:

        猜你喜欢
        • 2013-08-13
        • 1970-01-01
        • 2018-06-18
        • 2015-05-07
        • 2017-11-29
        • 2017-11-15
        • 2020-05-05
        • 2020-01-17
        • 1970-01-01
        相关资源
        最近更新 更多