无凭证调用AWS Amplify Storage.get访问公共S3资源报byteLength错误
问题背景
需要实现功能:未完成凭证登录的访客用户,可直接检索S3存储桶公共路径下存储的图片资源。
当前异常:在fetchNotes()方法中调用Storage.get(note.image)时,控制台抛出如下错误:
TypeError: Cannot read properties of undefined (reading 'byteLength')
已完成验证项:
- 持有合法登录凭证的用户调用
Storage.put()和Storage.get()方法均可正常执行 - 直接通过浏览器访问对应S3对象URL可正常读取公共路径资源,S3存储桶本身的公共读权限配置无误
- 初步判断异常由Amplify针对存储桶公共读访问的相关配置问题导致
相关代码
import React, { useState, useEffect } from 'react'; import './App.css'; import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react'; import { listNotes } from './graphql/queries'; import { createNote as createNoteMutation, deleteNote as deleteNoteMutation } from './graphql/mutations'; import { API, Storage } from 'aws-amplify'; const initialFormState = { name: '', description: '' } function App() { const [notes, setNotes] = useState([]); const [formData, setFormData] = useState(initialFormState); const { user, authStatus } = useAuthenticator((context) => [context.user, context.authStatus ]); useEffect(() => { fetchNotes(); }, []); async function deleteNote({ id }) { const newNotesArray = notes.filter(note => note.id !== id); setNotes(newNotesArray); await API.graphql({ query: deleteNoteMutation, variables: { input: { id } }, authMode: "AMAZON_COGNITO_USER_POOLS" }); } async function onChange(e) { console.log(e.target.files[0]); if (!e.target.files[0]) return const file = e.target.files[0]; setFormData({ ...formData, image: file.name }); await Storage.put(file.name, file); fetchNotes(); } async function fetchNotes() { console.log("ftech"); const apiData = await API.graphql({ query: listNotes }); const notesFromAPI = apiData.data.listNotes.items; await Promise.all(notesFromAPI.map(async note => { if (note.image) { const image = await Storage.get(note.image, {config: {level: 'public' }}).catch((err) => { console.error(err); }); if (image) { note.image = image; } } return note; })) setNotes(apiData.data.listNotes.items); console.log(notes); } async function createNote() { if (!formData.name || !formData.description) return; await API.graphql({ query: createNoteMutation, variables: { input: formData }, authMode: "AMAZON_COGNITO_USER_POOLS" }); if (formData.image) { const image = await Storage.get(formData.image); formData.image = image; } setNotes([...notes, formData]); setFormData(initialFormState); } return ( <div className="App"> <Authenticator> {({ signOut, user }) => ( <div> <h1>My Notes App</h1> <form onSubmit={createNote}> <input type="text" onChange={e => setFormData({ ...formData, 'name': e.target.value })} placeholder="Note name" value={formData.name} /> <input type="text" onChange={e => setFormData({ ...formData, 'description': e.target.value })} placeholder="Note description" value={formData.description} /> <input type="file" onChange={onChange} /> <button type="submit">Create Note</button> </form> <div> <h1>Hello {user.username}</h1> </div> <button onClick={signOut}>Sign out</button> </div> )} </Authenticator> <div style={{ marginBottom: 30 }}> { notes.map(note => ( <div key={note.id || note.name}> <h2>{note.name}</h2> <p>{note.description}</p> <p>{note.owner}</p> <p>{note.createdAt}</p> { (authStatus === 'authenticated') && (user.username === note.owner) && <button onClick={() => deleteNote(note)}>Delete note</button> } { note.image && <img src={note.image} style={{ width: 400 }} /> } </div> )) } </div> </div> ); } export default App;
排查思路
- 首先定位参数传参错误:当前
Storage.get的公共级别参数写法不符合Amplify API规范,嵌套config的写法在未登录状态下会触发SDK的签名逻辑,尝试读取不存在的用户身份凭证字段,最终抛出byteLength读取异常。 - 其次检查GraphQL接口的鉴权配置:
listNotes查询默认未指定鉴权模式时,未登录用户会被API拦截,根本拿不到完整的note列表数据。 - 最后校验Amplify存储配置的公共访问开关:未开启guest访问权限时,即使S3桶本身开了公共读,Amplify SDK也会默认给请求加签名逻辑,未登录状态下必然报错。
解决方案
- 修正
Storage.get的参数写法,根据你使用的Amplify版本选择对应写法:- Amplify V5版本直接传级别参数,不需要嵌套config:
const image = await Storage.get(note.image, { level: 'public' })- Amplify V6版本公共路径访问不需要传level,直接拼接公共路径前缀即可,未登录状态下会自动返回无签名的公开URL:
const image = await Storage.get(`public/${note.image}`) - 修改
listNotes请求的鉴权模式,允许未登录用户访问:const apiData = await API.graphql({ query: listNotes, authMode: 'AWS_IAM' }); - 更新Amplify存储配置,开启访客公共访问权限:
运行命令amplify update storage,在配置引导中选择开启Guest用户的读权限,之后执行amplify push同步配置到云端即可。 - 补充优化:
useEffect中调用fetchNotes时,需要把authStatus加入依赖数组,避免登录状态切换时图片URL不刷新的问题。
内容的提问来源于stack exchange,提问作者Dan0
相关产品推荐
相关产品推荐

