如何从 Node 中的图像 url 将图像上传到 Google Cloud Storage?

Posted

技术标签:

【中文标题】如何从 Node 中的图像 url 将图像上传到 Google Cloud Storage?【英文标题】:How to upload an image to Google Cloud Storage from an image url in Node? 【发布时间】:2016-08-08 06:59:22 【问题描述】:

给定一个图片网址,我如何将该图片上传到 Google Cloud Storage 以使用 Node.js 进行图片处理?

【问题讨论】:

【参考方案1】:

这是一个两步过程:

使用request或fetch在本地下载文件。

用官方library上传到协鑫。

var fs = require('fs');
var gcloud = require('gcloud');

// Authenticating on a per-API-basis. You don't need to do this if you auth on a
// global basis (see Authentication section above).

var gcs = gcloud.storage(
  projectId: 'my-project',
  keyFilename: '/path/to/keyfile.json'
);

// Create a new bucket.
gcs.createBucket('my-new-bucket', function(err, bucket) 
  if (!err) 
    // "my-new-bucket" was successfully created.
  
);

// Reference an existing bucket.
var bucket = gcs.bucket('my-existing-bucket');                
var localReadStream = fs.createReadStream('/photos/zoo/zebra.jpg');
var remoteWriteStream = bucket.file('zebra.jpg').createWriteStream();
localReadStream.pipe(remoteWriteStream)
  .on('error', function(err) )
  .on('finish', function() 
    // The file upload is complete.
  );

如果您想将文件保存为 jpeg 图像,则需要编辑 remoteWriteStream 流并添加自定义元数据:

var image = bucket.file('zebra.jpg');
localReadStream.pipe(image.createWriteStream(
    metadata: 
      contentType: 'image/jpeg',
      metadata: 
        custom: 'metadata'
      
    
))

我在挖掘 documentation 时发现了这个

【讨论】:

@Yevgen Safronov,谢谢,上传后如何获取响应网址 @SandeepJangir 不是 100% 确定,您可以通过连接存储桶的路径和存储桶中的文件路径来构造它,请参阅选项/目标以获取更多详细信息 googlecloudplatform.github.io/google-cloud-node/#/docs/… @SandeepJangir 我推荐使用 google-cloud 包。在 bucket.upload 函数的回调中,您可以对新上传的文件调用 getSignedUrl 以获取存储桶对象的临时 URL。阅读更多here。我不相信自述文件是有关此过程的详细说明,但实际代码中有 cmets 和示例 (IIRC) 可以为您指明正确的方向【参考方案2】:

为了补充 Yevgen Safronov 的答案,我们可以将请求通过管道传输到写入流中,而无需将图像显式下载到本地文件系统中。

const request = require('request');
const storage = require('@google-cloud/storage')();

function saveToStorage(attachmentUrl, bucketName, objectName) 
  const req = request(attachmentUrl);
  req.pause();
  req.on('response', res => 

    // Don't set up the pipe to the write stream unless the status is ok.
    // See https://***.com/a/26163128/2669960 for details.
    if (res.statusCode !== 200) 
      return;
    

    const writeStream = storage.bucket(bucketName).file(objectName)
      .createWriteStream(

        // Tweak the config options as desired.
        gzip: true,
        public: true,
        metadata: 
          contentType: res.headers['content-type']
        
      );
    req.pipe(writeStream)
      .on('finish', () => console.log('saved'))
      .on('error', err => 
        writeStream.end();
        console.error(err);
      );

    // Resume only when the pipe is set up.
    req.resume();
  );
  req.on('error', err => console.error(err));

【讨论】:

【参考方案3】:

如果处理来自远程 URL 的图像上传。参考谷歌文档提供的最新库。而不是存储图像的缓冲区。我们可以直接发送到存储。

function sendUploadUrlToGCS(req, res, next) 
  if (!req.body.url) 
    return next();
  

  var gcsname = Date.now() + '_name.jpg';
  var file = bucket.file(gcsname);

  return request(url: <remote-image-url>, encoding: null, function(err, response, buffer) 
    req.file = ;
    var stream = file.createWriteStream(
      metadata: 
        contentType: response.headers['content-type']
      
    );

    stream.on('error', function(err) 
       req.file.cloudStorageError = err;
       console.log(err);
       next(err);
    );

    stream.on('finish', function() 
      req.file.cloudStorageObject = gcsname;
      req.file.cloudStoragePublicUrl = getPublicUrl(gcsname);
      next();
    );

    stream.end(buffer);
  );

【讨论】:

【参考方案4】:

我使用了请求库和存储库来制作它。下面的代码在 TypeScript 中。问候

import * as gcs from '@google-cloud/storage';
import Storage from '@google-cloud/storage';
import request from 'request';

private _storage: Storage;

constructor() 
    // example of json path: ../../config/google-cloud/google-storage.json
    this._storage = new gcs.Storage(keyFilename: 'JSON Config Path');


public saveFileFromUrl(path: string): Promise<string> 
    return new Promise<any>((resolve, reject) => 
        request(url: path, encoding: null, (err, res, buffer) => 
            if (res.statusCode !== 200) 
                reject(err);
            
            const bucketName = 'bucket_name';
            const destination = `bucket location and file name`; // example: 'test/image.jpg'
            const file = this._storage.bucket(bucketName).file(destination);
            // put the image public
            file.save(buffer, public: true, gzip: true).then(data => 
                resolve(`$bucketName/$destination`)
            ).catch(err => 
                reject(err);
            );
        );
    )

【讨论】:

【参考方案5】:

将我的解决方案留在这里给想要使用的人:

firebase 管理 SDK axios 和异步等待
/**
 * TODO(developer): Uncomment the following lines before running the sample.
 * specify an existing bucket.
 * specify any url pointing to an file.
 */
// const bucketName = 'liist-prod-nodejs-backend';
// const url = "https://images.unsplash.com/photo-1601191906024-54b4e490abae?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=800&ixlib=rb-1.2.1&q=80&w=800";

// 1. load required packages
const axios = require('axios');
const crypto = require('crypto');
const httpAdapter = require('axios/lib/adapters/http');

// 2. setup firebase admin SDK + storage bucket
const admin = require('firebase-admin');
const serviceAccount = require('path/to/key.json');
const app = admin.initializeApp( 
  credential: admin.credential.cert(serviceAccount),
  storageBucket: bucketName
);
const storage = app.storage();
const bucket = storage.bucket(bucketName);

// 3. helper function
function randomToken(size = 20)  // maxsize is 128
  return crypto.randomBytes(64).toString('hex').substr(0, size)


// 4. async function to actually upload image from link to firebase storage bucket
async function uploadToStorage(bucket, url) 
  // define filename, folder and access token
  const accessToken = randomToken();
  const bucketName = bucket.name;
  const fileEnding = url.split('.').pop();
  const folder = 'defaultFolder';
  const filename = `myTargetFile`;
  const fullPath = `$folder/$filename.$fileEnding`;
  const fullPathUrlEncoded = `$folder%2F$filename.$fileEnding`;

  // axios request to get file stream
  const axiosResponse = await axios.get(url,  responseType: 'stream', adapter: httpAdapter );
  if (axiosResponse.status !== 200) 
    throw new Error(`axios request to $url failed.`);
   
  // create file + write stream (=> tweak options if needed)
  const file = bucket.file(fullPath);
  const output = file.createWriteStream(
    gzip: true,
    // if public is true, the file can be found here: `https://storage.googleapis.com/$bucketName/$fullPath`;
    public: false, // media token needed, more restricted and secure
    metadata: 
      contentType: axiosResponse.headers['content-type'],
      metadata: 
        firebaseStorageDownloadTokens: accessToken, // define access token
      ,
    
  );
  // wrapp stream around a promise
  // => resolves to public url
  const stream = axiosResponse.data;
  const streamPromise = new Promise(function (resolve, reject) 
    stream.on('data', (chunk) => 
      output.write(new Buffer.from(chunk));
    );
    stream.on('end', () => 
      output.end();
      const publicUrl = `https://firebasestorage.googleapis.com/v0/b/$bucketName/o/$fullPathUrlEncoded?alt=media&token=$accessToken`;
      resolve(publicUrl);
    );
    stream.on('error', (err) => 
      output.end();
      reject(err);
    )
  );
  return await streamPromise;


// 4. upload to storage
console.log("uploading file to storage ...");
(async () => 
  const publicUrl = await uploadToStorage(bucket, url);
  console.log(publicUrl);
)(); 

【讨论】:

【参考方案6】:

utility.js

 // google cloud stoage 
 let fs                = require('fs');
 const  Storage      = require('@google-cloud/storage');
 var credentials       = require('../../yourProjectCredentials.json');
 const storage         = new Storage(credentials: credentials);

 const bucketName      = 'pictures';

 const uuidV1          = require('uuid/v1');
 var dir               = './images';



/**
 * Store Image to GCP Buket
 * @param  picture 
 * @returns  picture_url 
 */
class ApplicationUtility

    constructor()

    /**
     * Store Image to GCP Buket
     * @param  picture 
     * @returns  picture_url 
     */

    async storeImageTocloud (picture) 

        let fileNamePic = uuidV1();
        let path2 = fileNamePic + "_picture.jpg";
        let path = dir + "/" + path2;
        var bitmap = new Buffer.from(picture, 'base64');
        fs.writeFileSync(path, bitmap,  flag: 'w' , (err) => 
            if (err)
                throw (err);
        );
        storage
            .bucket(bucketName)
            .upload(path)
            .then(() => console.log(`$fileNamePic uploaded to 
             $bucketName.`))
            .catch(err =>  throw (err) );

        let url = `https://storage.googleapis.com/$bucketName/$path2`;
        return url;

    




module.exports = ApplicationUtility;

index.js

  const ImagesStorage              = require('./utility');
  const imagesStorage              = new ImagesStorage();

        //call 
        let picture = body.pic
        let url = await imagesStorage.storeImageTocloud(picture);
        console.log(url)

【讨论】:

以上是关于如何从 Node 中的图像 url 将图像上传到 Google Cloud Storage?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用自定义 url 和文件夹将图像上传到 cloudinary

如何使用传入的图像 URL 将图像从 iOS 上传到 DropBox?

如何在上传后立即接收 Cloudinary 图像 URL(React JS 和 Node Js)?

如何使用 ios 应用程序将图像上传到 Node.JS 服务器

如何将图像上传到firebase存储然后将图像url存储在firestore中?

从 url 下载文件并将其上传到 AWS S3 而不保存 - node.js