试图检索存储在 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 的文件是有效的,这里有两种方法:
- Return 数据作为
base64
字符串而不是直接来自 S3 的缓冲区。您可以通过 returning as 在您的服务器端执行此操作
const base64MP3 = data.Body.toString('base64');
然后您可以将其传递给 src
属性 中的 MP3 播放器,它将播放音频。前缀为 data:audio/mpeg;base64
- 而不是 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.Body
的 data
属性中)
- 将
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());
我有一个反应网络应用程序,允许用户在浏览器中录制 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' blobconst 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 的文件是有效的,这里有两种方法:
- Return 数据作为
base64
字符串而不是直接来自 S3 的缓冲区。您可以通过 returning as 在您的服务器端执行此操作
const base64MP3 = data.Body.toString('base64');
然后您可以将其传递给 src
属性 中的 MP3 播放器,它将播放音频。前缀为 data:audio/mpeg;base64
- 而不是 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.Body
的data
属性中) - 将
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());