我的平台上传到谷歌云存储有一个问题,我只是在生产环境中遇到这个问题,这真是令人头疼。我的平台使用NodeJS,这个文件上传正在API服务的后端完成。
语言:Javascript,nodejs version 6.5
使用的套餐:@google-cloud/storage version 0.2.0
,gm 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!还是同样的问题。当我在本地运行平台但在生产中失败时工作得很好。
我还尝试为我的服务帐户提供更多可能解决问题的“角色”。没有任何区别。
这就是我到目前为止所做的一切。在这个问题上感觉真的被阻止所以我正在继续其他一些事情一两天,所以我可以获得一些有效的功能和错误修复。希望得到更多建议!
答案 0 :(得分:2)
如果代码在一个存储桶上工作但在另一个存储桶上失败,那么要检查的是您正在运行代码的服务帐户,以及该服务帐户是否有权写入当前失败的目标生产存储桶
这是一个链接,提供有关身份验证和范围到存储的信息: https://cloud.google.com/storage/docs/authentication
此外,还有一个关于服务帐户的链接: https://cloud.google.com/iam/docs/understanding-service-accounts
请告诉我这是否有帮助!
答案 1 :(得分:1)
我现在重写了没有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);
});