试图检索存储在 AWS S3 中的 mp3 文件并将其作为 Blob 加载到我的 React 客户端中......它不起作用

Trying to retrieve an mp3 file stored in AWS S3 and load it into my React client as a Blob...it's not working

我有一个反应网络应用程序,允许用户在浏览器中录制 mp3 文件。这些 mp3 文件保存在 AWS S3 存储桶中,可以在用户的​​下一个会话期间检索并加载回 React 应用程序。

保存文件工作正常,但是当我尝试使用 getObject() 检索文件并尝试在客户端创建一个 mp3 blob 时,我得到一个小的、无法使用的 blob:

这是录制的 mp3 文件的旅程:

1) 保存到 S3

在我的 Express/Node 服务器中,我收到上传的 mp3 文件并保存到 S3 存储桶:

//SAVE THE COMPLETED AUDIO TO S3
router.post("/", [auth, upload.array('audio', 12)], async (req, res) => {

    try {
        //get file
        const audioFile = req.files[0];

        //create object key
        const userId = req.user;
        const projectId = req.cookies.currentProject;
        const { sectionId } = req.body;
        const key = `${userId}/${projectId}/${sectionId}.mp3`;

        const fileStream = fs.createReadStream(audioFile.path)

        const uploadParams = {
            Bucket: bucketName,
            Body: fileStream,
            Key: key,
            ContentType: "audio/mp3" 
        }

        const result = await s3.upload(uploadParams).promise();
        
        res.send(result.key);

    } catch (error) {
        console.error(error);
        res.status(500).send();
    }
});

据我所知,现阶段没有问题。该文件以“类型:mp3”和“内容类型:audio/mp3”结束在我的 S3 存储桶中。

2) 从 S3 存储桶加载文件

加载 React 应用程序后,在我的 Express/Node 服务器中发出 HTTP GET 请求以从 S3 存储桶中检索 mp3 文件

//LOAD A FILE FROM S3
router.get("/:sectionId", auth, async(req, res) => {    

    try {
        //create key from user/project/section IDs
        const sectionId = req.params.sectionId;
        const userId = req.user;
        const projectId = req.cookies.currentProject;
        const key = `${userId}/${projectId}/${sectionId}.mp3`;

        const downloadParams = {
            Key: key,
            Bucket: bucketName
        }

        s3.getObject(downloadParams, function (error, data) {
            if (error) {
                console.error(error);
                res.status(500).send();
            }
            res.send(data);
        });

    } catch (error) {
        console.error(error);
        res.status(500).send();
    }
    
});

这里返回的“数据”是这样的:

3) 在客户端制作一个 Blob URL

最后,在 React 客户端中,我尝试从返回的数组缓冲区

创建一个 'audio/mp3' blob
const loadAudio = async () => {
        
        const res = await api.loadAudio(activeSection.sectionId);
        
        const blob = new Blob([res.data.Body], {type: 'audio/mp3' });
        
        const url = URL.createObjectURL(blob);
                
        globalDispatch({ type: "setFullAudioURL", payload: url });
}

创建的 blob 大小严重不足,似乎完全无法使用。下载文件导致 'Failed - No file' 错误。

我已经坚持了几天,但没有运气。如果您能提供任何建议,我将不胜感激!

谢谢

编辑 1

这里只是一些附加信息:在上传参数中,我将 Content-Type 明确设置为 audio/mp3。这是因为当未设置时,Content-Type 默认为 'application/octet-stream'。无论哪种方式,我都遇到了相同的问题和相同的结果。

编辑 2 应评论者的要求,这是调用完成后客户端可用的 res.data:

您声明:“创建的 blob 尺寸严重不足,似乎完全无法使用”

我认为您遇到了编码问题。从 Amazon S3 存储桶中读取 MP3 后,您需要对其进行正确编码,以便它在网页中运行。

我做了一个类似的多媒体用例,涉及 MP4 和 Java 应用程序。也就是说,我想要从存储桶中获取的 MP4 在网页中播放 - 如本示例网络应用所示。

从 S3 存储桶中读取字节流后,我必须对其进行编码,以便在 HTML 视频标签中播放。这里有一个很好的参考 properly encode a MP3 file.

我觉得你的服务器端代码没问题。我对客户端方法不是很清楚。您是否将其加载到 HTML5 音频播放器的 blob 中。

我有几种方法,假设您正在尝试将其加载到 UI 中的音频标签中。

<audio controls src="data:audio/mpeg;base64,blahblahblah or html src" />

假设您上传到 S3 的文件是有效的,这里有两种方法:

  1. Return 数据作为 base64 字符串而不是直接来自 S3 的缓冲区。您可以通过 returning as
  2. 在您的服务器端执行此操作
 const base64MP3 = data.Body.toString('base64');

然后您可以将其传递给 src 属性 中的 MP3 播放器,它将播放音频。前缀为 data:audio/mpeg;base64

  1. 而不是 return 整个 MP3 文件,让你的 sectionID 方法 return 一个 presigned S3 URL。本质上,这是对 S3 中的对象的直接 link,授权时间为 5 分钟。

那么你应该可以直接使用这个URL来播放音频 并将其设置为 src。请记住它会过期。

 const url = s3.getSignedUrl('getObject', {
        Bucket: myBucket,
        Key: myKey,
        Expires: signedUrlExpireSeconds
   });

根据客户端 res.data 的输出,您需要做几件事:

  • res.data.Body 的使用替换为 res.data.Body.data(因为实际数据数组位于 res.data.Bodydata 属性中)
  • Uint8Array 传递给 Blob 构造函数,因为现有数组是更大的类型,这将创建一个无效的 blob

把它们放在一起,你最终会替换:

const blob = new Blob([res.data.Body], {type: 'audio/mp3' });

与:

const blob = new Blob([new Uint8Array(res.data.Body.data)], {type: 'audio/mp3' });

说了这么多,潜在的问题是 NodeJS 服务器正在将内容作为来自 S3 的响应的 JSON 编码序列化发送,这对于您正在做的事情来说可能有点矫枉过正。相反,您可以直接发送 Buffer,这将涉及在服务器端替换:

res.send(data);

与:

res.set('Content-Type', 'audio/mp3');
res.send(data.Body);

并且在客户端(可能在 loadAudio 方法中)将响应处理为 blob 而不是 JSON。如果使用 Fetch API 那么它可以像这样简单:

const blob = await fetch(<URL>).then(x => x.blob());