Reputation: 8068
I upload mp3s to a bucket by creating a "signed upload url" via Google's getSignedUrl call:
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;
};
We then use Dropzone to upload it like this:
$('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'
},
...
})
The problem I am facing is that once the file is uploaded, it tends to get corrupted. Now, it will still play, but strange things happen, for instance if I try to seek to 10 seconds in, it plays the file from the beginning and everything is shifted ahead by 10 seconds.
I did a comparison between the source file, and the file after it is uploaded using ffmpeg. Here are the results:
Source 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
Uploaded 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
Notice the "Header missing" and the duration warnings. Gone is all the id3 info from the original.
I inspected the request that Dropzone is making (a PUT) to the uploadUrl and it's of the format:
-----------------------------84271646821941633862998010702
Content-Disposition: form-data; name="file"; filename="uncover-iUpa1OtA-20200317.mp3"
Content-Type: audio/mpeg
<File data...>
Can anyone think of a reason why this may be happening?
Upvotes: 0
Views: 1025
Reputation: 8068
Ok, figured this out! DropZone was sending a serialized FormData blob that included the MP3 as the body. However, the GCS signedUrl expects only the content of the file.
For instance, when I inspected the content of an uploaded MP3 file, I saw this:
What I did to fix this was to add a new option
to the Dropzone instance I was using called gcsUpload
. Whenever I'm uploading to GCS, and I am about to set the url for the file, I also set gcsUpload = true
.
Inside a sending
event handler I setup for Dropzone I do the following:
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);
}
}
});
Doing this sends only the file
data to the signedUrl, not the formdata.
Upvotes: 3