google-api - 使用 DropZone.js 通过 signedUrl 将 MP3 上传到 Google Cloud Storage 已损坏
问题描述
我通过 Google 的getSignedUrl调用创建“签名上传 url”,将 mp3 上传到存储桶:
const { Storage } = require('@google-cloud/storage');
const bucketName = 'uploads';
const storage = new Storage();
const uploadBucket = storage.bucket(bucketName);
const config = require('config');
const EXPIRES_SECONDS = 600;
exports.generateUploadLink = async (req, res) => {
let contentId = req.query.contentId || req.body.contentId;
let file = uploadBucket.file(`${contentId}.mp3`);
try {
let signedUrl = await file.getSignedUrl({
action: 'write',
expires: Date.now() + (EXPIRES_SECONDS * 1000),
contentType: 'audio/mpeg'
});
res.status(200).json({meta: { status: 'OK'});
} catch(err) {
console.log(`Error while getting signed url: ${err}`);
res.status(500).json({meta: { status: 'FAIL', message: `Could not generate signed upload url for: ${contentId}`, error: err}});
}
return;
};
然后我们使用Dropzone像这样上传它:
$('div#mp3DropzoneArea').dropzone({
url: uploadUrl,
previewTemplate: template,
autoProcessQueue: false,
createImageThumbnails: false,
method: 'put',
clickable: this.get('clickElementSelector'),
filesizeBase: 1024,
maxFilesize: 250, // MB
acceptedFiles: '.mp3',
dictDefaultMessage: this.get('defaultMessage'),
dictInvalidFileType: 'Invalid file type. Only .mp3 files can be imported.',
parallelUploads: 1,
maxFiles: this.get('maxFiles'),
headers: {
'Content-Type': 'audio/mpeg'
},
...
})
我面临的问题是,一旦文件上传,它往往会被损坏。现在,它仍然会播放,但会发生奇怪的事情,例如,如果我尝试搜索 10 秒,它会从头开始播放文件,并且一切都会提前 10 秒。
我对源文件和使用ffmpeg上传后的文件进行了比较。结果如下:
源 MP3
$ ffmpeg -i uncover-iUpa1OtA-20200317.mp3
ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 9.2.1 (GCC) 20200122
configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
libavutil 56. 31.100 / 56. 31.100
libavcodec 58. 54.100 / 58. 54.100
libavformat 58. 29.100 / 58. 29.100
libavdevice 58. 8.100 / 58. 8.100
libavfilter 7. 57.100 / 7. 57.100
libswscale 5. 5.100 / 5. 5.100
libswresample 3. 5.100 / 3. 5.100
libpostproc 55. 5.100 / 55. 5.100
Input #0, mp3, from 'uncover-iUpa1OtA-20200317.mp3':
Metadata:
comment : From the '60s to the '90s, parents worried messages hidden in rock albums would make their children do drugs and worship the devil. The truth could only be revealed if these records were played backwards. Twenty Thousand Hertz — a podcast about the w
:
:
album : Uncover
title : Bonus: Hidden Messages, Backmasking and the Satanic Panic
artist : Canadian Broadcasting Corporation
track : 1
date : 2020
Duration: 00:29:17.86, start: 0.000000, bitrate: 129 kb/s
Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 128 kb/s
Stream #0:1: Video: mjpeg (Progressive), yuvj420p(pc, bt470bg/unknown/unknown), 1400x1400 [SAR 1:1 DAR 1:1], 90k tbr, 90k tbn, 90k tbc (attached pic)
Metadata:
comment : Other
上传的 MP3
$ ffmpeg -i uncover-uploaded-51ebf5bb-e4ea-4372-b410-d90b04abec6a.mp3
ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 9.2.1 (GCC) 20200122
configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
libavutil 56. 31.100 / 56. 31.100
libavcodec 58. 54.100 / 58. 54.100
libavformat 58. 29.100 / 58. 29.100
libavdevice 58. 8.100 / 58. 8.100
libavfilter 7. 57.100 / 7. 57.100
libswscale 5. 5.100 / 5. 5.100
libswresample 3. 5.100 / 3. 5.100
libpostproc 55. 5.100 / 55. 5.100
[mp3float @ 0000013aa370d800] Header missing
Last message repeated 1 times
[mp3 @ 0000013aa370bdc0] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from 'uncover-uploaded-51ebf5bb-e4ea-4372-b410-d90b04abec6a.mp3':
Duration: 00:29:39.25, start: 0.000000, bitrate: 127 kb/s
Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 128 kb/s
At least one output file must be specified
请注意“缺少标题”和持续时间警告。原版中的所有 id3 信息都一去不复返了。
我检查了 Dropzone 向 uploadUrl 发出的请求(PUT),它的格式为:
-----------------------------84271646821941633862998010702
Content-Disposition: form-data; name="file"; filename="uncover-iUpa1OtA-20200317.mp3"
Content-Type: audio/mpeg
<File data...>
谁能想到可能发生这种情况的原因?
解决方案
好的,想通了!DropZone 正在发送包含 MP3 作为正文的序列化 FormData blob。但是,GCS signedUrl 只需要文件的内容。
例如,当我检查上传的 MP3 文件的内容时,我看到了:
我为解决这个问题所做的就是向option
我正在使用的 Dropzone 实例添加一个新的名为gcsUpload
. 每当我上传到 GCS,并且我要设置文件的 url 时,我也会设置gcsUpload = true
.
在sending
我为 Dropzone 设置的事件处理程序中,我执行以下操作:
dropzone.on('sending', function(file, xhr,/* formData*/) {
// for Google Cloud Storage we don't want to send formData
// as that information will be serialized into the file itself.
// Instead, we only want to send the file to the signedUrl
if (dropzone.options.gcsUpload) {
let _send = xhr.send;
xhr.send = function() {
_send.call(xhr, file);
}
}
});
这样做只会将file
数据发送到signedUrl,而不是formdata。
推荐阅读
- css - 在以下情况下,Router Link Active 不起作用,它们是否有任何替代方式来动态激活按钮?
- c++ - 错误 C1903 - 调用 offsetof 时无法从先前的错误中恢复
- php - 为什么 flashdata 在 Codeigniter 中不起作用
- python - 使用 pandas 数据框样式器更正行着色
- python - 为 Django CreateView 应用搜索/过滤下拉列表
- c - 这是释放作为结构一部分的多个动态分配的数组的正确方法吗?
- firebase - 使用 Firebase 使用推送 ID 时如何检查现有数据?
- vue.js - 访问动态子组件
- matlab - 在 MATLAB 中绘制时间序列数据
- javascript - NextJS 的部署策略建议