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

问题描述

我有一个反应网络应用程序,允许用户在浏览器中录制 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();
    }
});

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

2) 从 S3 存储桶加载文件

当加载反应应用程序时,在我的 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 客户端中,我尝试从返回的数组缓冲区创建一个“音频/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 尺寸严重不足,似乎完全无法使用。下载文件会导致“失败 - 无文件”错误。 在此处输入图像描述

我已经坚持了几天了,没有运气。我将非常感谢您提供的任何建议!

谢谢

编辑 1

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

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

在此处输入图像描述

标签: node.jsreactjsamazon-web-servicesamazon-s3aws-sdk

解决方案


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

  • res.data.Body用with替换使用res.data.Body.data(因为实际的数据数组在 的data属性中res.data.Body
  • 将 a 传递Uint8ArrayBlob构造函数,因为现有数组的类型较大,这将创建无效的 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());

推荐阅读