You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shopify后端图片上传异常:状态停滞上传中,求解决方案

Shopify图片上传卡在Uploading状态的解决方案

你的代码存在三个核心问题,导致上传后后台显示失败、状态无法从Uploading推进到Processing/Ready:

1. 跳过了实际文件上传到Staged Target的关键步骤

调用stagedUploadsCreate仅获取了Shopify提供的临时上传地址,但未将本地文件的二进制内容POST到该地址。Shopify未收到文件实体,自然会一直卡在Uploading状态,最终标记上传失败。

2. fileCreate的变量格式错误

你传入的files是单个对象,但GraphQL接口要求该参数为数组类型,格式不匹配会导致文件创建逻辑异常。

3. 前端未提交实际文件内容

当前前端仅提交了文件名、类型、大小等元数据,后端无法获取到要上传的文件二进制数据,根本无法完成实际上传操作。


具体修复步骤

步骤1:前端修改提交逻辑,传递实际文件

更新generateProduct方法,用FormData封装文件实体后提交:

const generateProduct = () => {
  if (!files[0]) return;
  
  const formData = new FormData();
  formData.append("file", files[0]);
  formData.append("filename", files[0].name);
  formData.append("filetype", files[0].type);
  formData.append("filesize", files[0].size.toString());
  
  submit(formData, { replace: true, method: "POST" });
};

步骤2:后端处理FormData,完成文件上传到Staged Target

修改action函数,先解析FormData拿到文件,再将文件POST到Staged Target地址,最后修复fileCreate的参数格式:

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;
  const name = formData.get("filename") as string;
  const type = formData.get("filetype") as string;
  const size = formData.get("filesize") as string;

  const prepareFiles = (files: { name: string; type: string; size: string; }[]) =>
    files.map((file) => ({
        filename: file.name,
        mimeType: file.type,
        resource: file.type.includes("image") ? "IMAGE" : "FILE",
        fileSize: file.size,
        httpMethod: "POST",
    }));

  const preparedFiles = prepareFiles([{ name, type, size }]);

  // 1. 获取Staged Upload目标地址
  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];
  
  // 检查Staged Upload是否存在错误
  if (uploadFileJson.data.stagedUploadsCreate.userErrors.length > 0) {
    return json({ error: uploadFileJson.data.stagedUploadsCreate.userErrors });
  }

  // 2. 关键操作:将文件上传到Staged Target的URL
  const uploadForm = new FormData();
  // 添加Staged Target返回的参数
  stagedTarget.parameters.forEach(param => {
    uploadForm.append(param.name, param.value);
  });
  // 添加文件实体
  uploadForm.append("file", file);

  await fetch(stagedTarget.url, {
    method: "POST",
    body: uploadForm,
  });

  // 3. 修复fileCreate参数格式:files改为数组
  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();
  
  // 检查fileCreate是否存在错误
  if (fileCreateJson.data.fileCreate.userErrors.length > 0) {
    return json({ error: fileCreateJson.data.fileCreate.userErrors });
  }

  return json({
      stagedUpload: uploadFileJson,
      fileCreate: fileCreateJson,
      resourceurl: stagedTarget.resourceUrl
  });
};

步骤3:添加错误提示逻辑

更新前端useEffect,根据返回结果显示成功/失败提示:

useEffect(() => {
  if (actionData) {
    if (actionData.error) {
      shopify.toast.show(`上传失败: ${actionData.error.map(e => e.message).join(", ")}`);
    } else {
      shopify.toast.show("图片上传成功");
    }
  }
}, [actionData]);

额外排查建议

  1. 上传后打印fileCreateJson.data.fileCreate.files[0].fileErrors,可获取具体的文件错误信息,辅助排查问题。
  2. 如需支持多文件上传,需循环处理每个文件的Staged Upload和实体上传步骤。
  3. 可添加文件上传进度监听,提升用户体验。

内容的提问来源于stack exchange,提问作者mohit vamja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:41:04