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

如何在React组件中获取Transloadit上传至GCS的最终链接并解决状态异常

问题描述

我在React组件中使用Uppy+Transloadit上传文件到Google Cloud Storage(最终链接以https://storage.googleapis.com/...开头),遇到两个问题:

  • 无法清晰找到获取最终文件链接的方法,相关文档说明不够明确
  • 文件明明已经成功上传,但Transloadit对象的ok字段始终返回"ASSEMBLY_UPLOADING",而非预期的"ASSEMBLY_COMPLETED"

相关代码如下:

uppy.on('complete', (result) => {
  console.log(result)
})

UppyDashboard.tsx

'use client'

import themeModeState from '@/recoils/themeMode/themeModeState'
import '@uppy/core/dist/style.css'
import '@uppy/dashboard/dist/style.css'
import { Dashboard } from '@uppy/react'
import '@uppy/url/dist/style.css'
import * as React from 'react'
import { useRecoilValue } from 'recoil'
import { useEffect, useState } from 'react'
import createUppy from '@/components/uppy/createUppy'

export interface IUppyDashboard {
 // ...
}

const UppyDashboard: React.FC<IUppyDashboard> = ({
  templateId,
  authKey,
  authSecret,
}) => {
  const themeMode = useRecoilValue(themeModeState)
  const houseId = 'mainHouse'
  const roomId = 'mainRoom'
  const [uppy] = useState(() =>
    createUppy({
      ids: {
        houseId,
        roomId,
      },
      authKey,
      authSecret,
      templateId,
    })
  )

  useEffect(() => {
    if (houseId && roomId) {
      uppy.setOptions({ meta: { houseId, roomId } })
    }
  }, [uppy, houseId, roomId])

  uppy.on('complete', (result) => {
    console.log(result)
  })

  return (
          <Dashboard
            uppy={uppy}
            plugins={[
              // 'Webcam',
              'Dropbox',
              'GoogleDrive',
              'Facebook',
              'OneDrive',
              'Box',
              'Url',
            ]}
            metaFields={[
              { id: 'name', name: 'Name', placeholder: 'Image name' },
              {
                id: 'caption',
                name: 'Caption',
                placeholder: 'Describe what the image is about',
              },
            ]}
            showLinkToFileUploadResult={false} 
            waitForThumbnailsBeforeUpload={true} 
            showSelectedFiles={true}
            replaceTargetContent={true}
            showProgressDetails={false} 
            note="Images only, 1–4 files, up to 5 MB"
            height="350px" 
            width="auto" 
            theme={themeMode || 'auto'} 
          />
  )
}

export default UppyDashboard

createUppy.tsx

import Compressor from '@uppy/compressor'
import Uppy from '@uppy/core'
import Url from '@uppy/url'
import GoogleDrive from '@uppy/google-drive'
import Facebook from '@uppy/facebook'
import OneDrive from '@uppy/onedrive'
import Dropbox from '@uppy/dropbox'
import Box from '@uppy/box'
import ImageEditor from '@uppy/image-editor'
// @ts-ignore
import Hebrew from '@uppy/locales/lib/he_IL'
import Transloadit, { COMPANION_URL } from '@uppy/transloadit'
import expires from './dateString'
import createSignature from './transloaditSignature'

type CreateUppy = {
  ids: {
    houseId: string
    roomId: string
  }
  authKey: string
  authSecret: string
  templateId: string
}

const createUppy = (props: CreateUppy) => {
  const { ids, authKey, authSecret, templateId } = props
  const { houseId, roomId } = ids

  if (!authKey || !authSecret) {
    throw new Error('Missing auth key or secret')
  }

  const signature = createSignature({
    authKey,
    authSecret,
    expires,
    templateId,
  })

  // Adding to global `meta` will add it to every file.
  // Every Uppy instance needs a unique ID.
  return new Uppy({
    id: roomId,
    meta: { houseId, roomId },

    locale: Hebrew,
    autoProceed: false,
    restrictions: {
      allowedFileTypes: ['image/*'],
      maxNumberOfFiles: 4,
      minNumberOfFiles: 1,
      minFileSize: 1000,
      maxFileSize: 5 * 1024 * 1024, // 5 MB
    },
  })
    .use(Transloadit, {
      // Import files from Companion using the /import-files Robot
      // importFromUploadURLs: true,
      assemblyOptions(file: any) {
        return {
          params: {
            auth: { key: authKey, expires },
            template_id: templateId,
          },
          signature,
          // You can use these inside your template
          // https://transloadit.com/docs/topics/assembly-instructions/#form-fields-in-instructions
          fields: { roomId: file.meta.roomId, houseId: file.meta.houseId },
          // waitForEncoding: true,
        }
      },
    })
    .use(Compressor)
    .use(Url, { companionUrl: COMPANION_URL })
    .use(GoogleDrive, { companionUrl: COMPANION_URL })
    .use(Facebook, { companionUrl: COMPANION_URL })
    .use(OneDrive, { companionUrl: COMPANION_URL })
    .use(Dropbox, { companionUrl: COMPANION_URL })
    .use(Box, { companionUrl: COMPANION_URL })
}

export default createUppy

解决方法

1. 修复ASSEMBLY_UPLOADING状态问题

当前代码中Transloadit插件的waitForEncoding选项被注释,导致Uppy仅在文件上传完成后就触发complete事件,而非等待Transloadit完成所有编码/导出流程。

修改createUppy.tsx中的Transloadit配置:

.use(Transloadit, {
  assemblyOptions(file: any) {
    return {
      params: {
        auth: { key: authKey, expires },
        template_id: templateId,
      },
      signature,
      fields: { roomId: file.meta.roomId, houseId: file.meta.houseId },
      waitForEncoding: true, // 取消注释并设置为true
    }
  },
})

开启该选项后,Uppy会等待Transloadit完成整个Assembly流程(包括上传到GCS),此时ok字段会正确返回"ASSEMBLY_COMPLETED"。

2. 获取Google Cloud Storage最终文件链接

开启waitForEncoding后,complete事件的result对象中,每个文件的transloadit.assembly.results会包含Transloadit的处理结果,其中就有GCS的存储链接。

修改UppyDashboard.tsx中的complete事件监听:

useEffect(() => {
  const handleComplete = (result) => {
    result.successful.forEach(file => {
      const assemblyResults = file.transloadit?.assembly?.results;
      if (assemblyResults) {
        // 替换为你Transloadit模板中导出到GCS的Robot名称
        const gcsExportResults = assemblyResults.your_export_robot_name;
        if (gcsExportResults && gcsExportResults.length > 0) {
          const gcsUrl = gcsExportResults[0].url;
          console.log('GCS文件链接:', gcsUrl);
          // 可将链接存入状态或发送至后端
        }
      }
    });
  };

  uppy.on('complete', handleComplete);
  return () => uppy.off('complete', handleComplete); // 组件卸载时移除监听
}, [uppy]);

注意:your_export_robot_name需要替换为你Transloadit模板中,用于将文件导出到GCS的Robot名称(即模板中该步骤的name属性值)。

额外注意事项

  • 确保Transloadit模板已正确配置Google Cloud Storage导出步骤,且Assembly能正常完成导出
  • 检查expires变量的时间范围,避免在Assembly完成前签名过期
  • 在React组件中,务必将事件监听放在useEffect中,避免重复绑定

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 09:55:57