Google存储桶文件上传无法在生产环境中使用

时间:2017-12-23 02:08:01

标签: node.js file-upload google-cloud-platform google-cloud-storage

我的平台上传到谷歌云存储有一个问题,我只是在生产环境中遇到这个问题,这真是令人头疼。我的平台使用NodeJS,这个文件上传正在API服务的后端完成。

语言:Javascript,nodejs version 6.5

使用的套餐:@google-cloud/storage version 0.2.0gm version 1.23.0

该服务基本上将图像放在base 64中,创建图像的缓冲区,然后将该图像传送到google cloud bucket。容易腻。

不幸的是它不能用于生产,我无法弄清楚原因。完全相同的代码在生产中运行,它只指向一个不同的桶。

文件顶部的一些相关代码

var gm = require('gm').subClass({ imageMagick: true });
var Promise = require('bluebird');
var Q = require('q');
var request = require('request').defaults({ encoding: null });
var gcs = require('@google-cloud/storage')({
      keyFilename: sails.config.gcloud.keyFileName,
      projectId: sails.config.gcloud.projectId
});
var bucket = gcs.bucket(sails.config.gcloud.buckets.cdn);

// Takes raw base64Image from client and converts it into:
// {
//      meta: data:image/jpeg;base64,
//      data: hugeStringOfImageDataInBase64,
//      dateType: image/jpeg
// }
function decodeImage(base64Image) {
    // Not using regex due to perf.
    var startImageData = base64Image.indexOf(',');
    var regex = /^data:(.+);base64$/;
    var meta = base64Image.slice(0, startImageData);
    var data = base64Image.slice(startImageData + 1);
    var dataType = meta.match(regex)[1];

    return { type: dataType, meta: meta, data: data };
}

正在创建缓冲区的相关代码

createNewPictureAndUploadToCDNBase: function(imageInBase64, imageBuffer, cb) {

    return Picture.create()
        .then(function(emptyPicture) {
            var buffer;

            if (imageInBase64) {
                buffer = PictureService.bufferFromBase64(imageInBase64);
            } else {
                buffer = imageBuffer;
            }

            return PictureService.resizeAndUploadPictureToCDN(emptyPicture.id, buffer, function (err, results) {

                emptyPicture = _.merge(emptyPicture, results);

                return emptyPicture.save(function(err) {
                    cb(err, emptyPicture);
                });
            });

        }).catch(function(err) {
            return cb(err);
        });
},

bufferFromBase64: function (imageInBase64) {
    return new Buffer(decodeImage(imageInBase64).data, 'base64');
},

将文件传输到Google云端存储分区的代码的主要部分

resizeAndUploadPictureToCDN: function (pictureId, imageBuffer, cb) {

    var resizingOptions = {
        original: {
            quality: 80,
            size: {}
        },
        thumbnail: {
            quality: 75,
            size: {
                width: 60,
                height: 60
            }
        },
        medium: {
            quality: 65,
            size: {height: 250}
        }
    };

    var resizingPromises = _.mapValues(resizingOptions, function (options, resizingName) {

        return new Promise(function (resolve, reject) {
            var file = bucket.file(pictureId + '-' + resizingName);

            // NOTE: 'gm' is a call to GraphicMagick for node
            // More can be found here: https://github.com/aheckmann/gm

            gm(imageBuffer)
                .quality(options.quality)
                .resize(options.size.width || null, options.size.height || null)
                .stream()
                .pipe(file.createWriteStream())
                .on('finish', function(err, success) {

                    file.getMetadata(function(err, metadata) {
                        if (err) {
                            console.log("Error getting metadata from google cloud", err);
                            return reject(err);
                        }

                        resolve(metadata.mediaLink);
                    });

                }).on('error', function(err) {
                    console.log("Got an error uploading to Cloud Storage:", err);
                    reject(err);
                });
        });
    });

    Promise.props(resizingPromises)
        .then(function (results) {
            cb(null, results);
        }).error(cb);
},

不要担心调整语言的大小,我相信这部分不是问题。只需拍摄图像并上传不同尺寸的不同版本。我更有信心在本地工作与在文件流上工作相同,并与Google云端存储进行交互。

所以是的。当我在本地运行,上传到一个桶时,它运行良好。当它在生产中运行时,使用不同的存储桶,无法上传图像。

但是,在这两种情况下,我都没有收到错误消息。相反,当它失败时,我仍然知道文件已成功上传。就在我访问该文件的位置时,在生产时它完全没有任何数据。

file.getMetaData() 下的console.log的生产环境中读出

Corresponding metadata:  
{
    kind: 'storage#object',
    id: 'cdn.texasca.com/5a3d938b84d560010012b0b3-thumbnail/1513984907989412',
    selfLink: 'https://www.googleapis.com/storage/v1/b/cdn.texasca.com/o/5a3d938b84d560010012b0b3-thumbnail',
    name: '5a3d938b84d560010012b0b3-thumbnail',
    bucket: 'cdn.texasca.com',
    generation: '1513984907989412',
    metageneration: '1',
    timeCreated: '2017-12-22T23:21:47.915Z',
    updated: '2017-12-22T23:21:47.915Z',
    storageClass: 'STANDARD',
    timeStorageClassUpdated: '2017-12-22T23:21:47.915Z',
    size: '0',
    md5Hash: '1B2M2Y8AsgTpgAmY7PhCfg==',
    mediaLink: 'https://www.googleapis.com/download/storage/v1/b/cdn.texasca.com/o/5a3d938b84d560010012b0b3-thumbnail?generation=1513984907989412&alt=media',
    crc32c: 'AAAAAA==',
    etag: 'CKSji6XhntgCEAE=' 
}

请注意文件大小为0时如何显示size '0'。其他所有内容都与完全相同,就像从本地环境将文件流式传输到其他Google存储桶后一样除了它的文件大小如'13908'或某些文件大小,当你访问mediaLink位置时,它有一个完整尺寸的图片,而不仅仅是没有。

我在调试时遇到了麻烦...

我应该从哪里开始调试这样的问题?是否有常用的调试文件流的方法?有谁知道为什么我没有收到来自此文件流的错误消息?

真的试图避免完全重写我平台的这一部分。

非常感谢任何和所有帮助!

我在初步发布问题后尝试过的事情

所以为了解决这个问题,到目前为止我已经尝试了一些方法。第一个是为服务帐户创建新密钥。所以我这样做了,在我的代码中链接了该密钥并尝试了它。同样的问题,在本地开发模式下工作,在生产中不起作用(得到奇怪的非错误,它不会抛出错误,但对象大小 0 )。

另外,正如我所提到的,有两个桶。一个名为dev0.cdn.texasca.com,另一个名为cdn.texasca.com。在本地开发模式中,我将其切换为上传图像到cdn.texasca.com,而不是开发者和... BOOM!还是同样的问题。当我在本地运行平台但在生产中失败时工作得很好。

我还尝试为我的服务帐户提供更多可能解决问题的“角色”。没有任何区别。

这就是我到目前为止所做的一切。在这个问题上感觉真的被阻止所以我正在继续其他一些事情一两天,所以我可以获得一些有效的功能和错误修复。希望得到更多建议!

2 个答案:

答案 0 :(得分:2)

如果代码在一个存储桶上工作但在另一个存储桶上失败,那么要检查的是您正在运行代码的服务帐户,以及该服务帐户是否有权写入当前失败的目标生产存储桶

这是一个链接,提供有关身份验证和范围到存储的信息: https://cloud.google.com/storage/docs/authentication

此外,还有一个关于服务帐户的链接: https://cloud.google.com/iam/docs/understanding-service-accounts

请告诉我这是否有帮助!

答案 1 :(得分:1)

问题 生产环境中的GraphicMagick出现了问题。仍然没有弄清楚到底是什么,但解决方案是在没有GraphicMagick的情况下重写文件上传。

我现在重写了没有GraphicMagick的代码。

        return new Promise(function (resolve, reject) {
            var file = bucket.file(pictureId + '-' + resizingName);

            var stream = file.createWriteStream();

            stream.on('error', function (err) {
                if (err) {
                    console.log("Got an error uploading to GBucket: ", err);
                    return reject(err);
                }
            });

            stream.on('finish', function() {

                console.log("Uploaded successfully!!");

                file.getMetadata(function(err, metadata) {
                    if (err) {
                        console.log("Error getting metadata from google cloud", err);
                        return reject(err);
                    }

                    console.log("File's metadata: ", metadata);

                    return resolve(metadata.mediaLink);
                });     
            });

            stream.end(imageBuffer);
        });