基于Remix和TypeScript的Shopify应用图片上传问题求助
Shopify Remix+TypeScript图片上传处理错误修复方案
问题描述
使用Remix+TypeScript开发Shopify自定义应用的图片上传功能,前端可触发上传操作,但后台显示「1.png Processing error」,上传仅完成第一步,处理和就绪阶段失败。
核心问题分析
你的代码只完成了获取Shopify上传预签名地址这一步,但没有把实际的文件内容上传到该地址,导致Shopify无法找到文件进行后续处理,最终触发Processing error。具体问题点:
- 前端仅提交了文件名、类型、大小,未传递实际文件内容
- 后端拿到预签名地址后,没有执行文件上传到该地址的操作
- 直接用空的resourceUrl调用fileCreate,自然无法完成处理
修复步骤及代码修改
1. 前端修改:用FormData传递实际文件
修改generateProduct函数,通过FormData提交文件,而不是普通键值对:
const generateProduct = () => { if (!files[0]) return; const formData = new FormData(); formData.append("file", files[0]); submit(formData, { replace: true, method: "POST" }); };
2. 后端Action修改:解析FormData并上传文件到预签名地址
更新action函数,完成文件上传到staged target的步骤:
export const action = async ({ request }: ActionFunctionArgs) => { const { admin } = await authenticate.admin(request); // 解析FormData获取实际文件 const formData = await request.formData(); const file = formData.get("file") as File; if (!file) { return json({ error: "No file provided" }, { status: 400 }); } // 准备staged上传参数 const preparedFiles = [ { filename: file.name, mimeType: file.type, resource: file.type.includes("image") ? "IMAGE" : "FILE", fileSize: file.size.toString(), httpMethod: "POST", }, ]; // 获取预签名上传地址 const uploadFileResponse = await admin.graphql( `#graphql mutation stagedUploadsCreate($input: [StagedUploadInput!]!) { stagedUploadsCreate(input: $input) { stagedTargets { resourceUrl url parameters { name value } } userErrors { field message } } } `, { variables: { input: preparedFiles } }, ); const uploadFileJson = await uploadFileResponse.json(); const stagedTarget = uploadFileJson.data.stagedUploadsCreate.stagedTargets[0]; // 检查是否有用户错误 const userErrors = uploadFileJson.data.stagedUploadsCreate.userErrors; if (userErrors.length > 0) { return json({ error: userErrors }, { status: 400 }); } // 构建上传表单数据,包含预签名参数 const uploadFormData = new FormData(); stagedTarget.parameters.forEach((param: { name: string; value: string }) => { uploadFormData.append(param.name, param.value); }); uploadFormData.append("file", file); // 上传文件到Shopify的预签名地址 await fetch(stagedTarget.url, { method: "POST", body: uploadFormData, }); // 调用fileCreate创建文件记录 const fileCreateResponse = await admin.graphql( `#graphql mutation fileCreate($files: [FileCreateInput!]!) { fileCreate(files: $files) { files { alt createdAt fileStatus fileErrors { code details message } preview { image { url } status __typename } ... on GenericFile { id } ... on Video { id } } userErrors { code field message } } }`, { variables: { files: [ { alt: "Image", contentType: "IMAGE", originalSource: stagedTarget.resourceUrl, }, ], }, }, ); const fileCreateJson = await fileCreateResponse.json(); return json({ stagedUpload: uploadFileJson, fileCreate: fileCreateJson, resourceUrl: stagedTarget.resourceUrl, }); };
3. 额外优化:添加错误处理
在前端添加对action返回错误的处理,替换原有的useEffect:
useEffect(() => { if (actionData) { if (actionData.error) { shopify.toast.show(`上传失败: ${JSON.stringify(actionData.error)}`); } else { shopify.toast.show("图片上传成功"); } } }, [actionData]);
关键说明
- Shopify的staged上传流程分为三步:先获取预签名地址,再把文件上传到该地址,最后调用fileCreate关联资源
- 必须传递实际文件内容到预签名地址,否则Shopify无法处理文件
- 要注意FormData的正确使用,前端和后端都要对应解析
内容的提问来源于stack exchange,提问作者mohit vamja
相关产品推荐
相关产品推荐

