mcpField
I
integrate-uppy-transloadit-s3-uploading-to-nextjs

integrate-uppy-transloadit-s3-uploading-to-nextjs

github.com/transloadit开发590 次安装4 天前更新
代码仓库

关于 integrate-uppy-transloadit-s3-uploading-to-nextjs

这项技能指导在 Next.js 应用中接入 Uppy 和 Transloadit 上传,包括服务端签名和浏览器上传面板。还介绍了可选的 Transloadit 模板,用于调整图片尺寸并导出到 S3。

主要功能

  1. 01服务端签署 Assembly 选项
  2. 02Uppy Dashboard 上传界面
  3. 03集成 Transloadit 插件
  4. 04可选图片缩放和 S3 导出
  5. 05将 Transloadit 密钥保留在服务端

适用场景

  1. 01为 Next.js 应用添加文件上传
  2. 02签署上传选项且不暴露密钥
  3. 03缩放上传图片并导出到 S3

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/transloadit/skills --skill integrate-uppy-transloadit-s3-uploading-to-nextjs

安装后在 Claude Code 中输入/integrate-uppy-transloadit-s3-uploading-to-nextjs确认已加载。

详细介绍

输入

  • 必需的环境变量(仅服务端):TRANSLOADIT_KEY、TRANSLOADIT_SECRET
  • 可选的环境变量:TRANSLOADIT_TEMPLATE_ID(创建模板后建议设置)

本地开发时,将这些变量写入 .env.local。切勿向浏览器公开 TRANSLOADIT_SECRET。

安装

npm i @transloadit/utils @uppy/core @uppy/dashboard @uppy/transloadit

实现(推荐路径)

选择根目录:

  • 如果项目中有 src/app,使用 src/app/...
  • 否则使用 app/...

1) 服务端:向浏览器返回已签名的 Assembly 选项

创建 app/api/transloadit/assembly-options/route.ts(如果使用 src/,则创建 src/app/api/transloadit/assembly-options/route.ts):

import { NextResponse } from 'next/server'
import { signParamsSync } from '@transloadit/utils/node'

export const runtime = 'nodejs'

function reqEnv(name: string): string {
  const v = process.env[name]
  if (!v) throw new Error(`Missing required env var: ${name}`)
  return v
}

function formatExpiresUtc(minutesFromNow: number): string {
  const ms = Date.now() + minutesFromNow * 60_000
  return new Date(ms).toISOString().replace(/\.\d{3}Z$/, 'Z')
}

export async function POST() {
  try {
    const authKey = reqEnv('TRANSLOADIT_KEY')
    const authSecret = reqEnv('TRANSLOADIT_SECRET')
    const templateId = process.env.TRANSLOADIT_TEMPLATE_ID

    const params: Record<string, unknown> = {
      auth: { key: authKey, expires: formatExpiresUtc(30) },
    }

    if (templateId) {
      params.template_id = templateId
    } else {
      // Minimal "known good" steps (works without pre-creating a template).
      params.steps = {
        resized: {
          robot: '/image/resize',
          use: ':original',
          width: 320,
        },
      }
    }

    const paramsString = JSON.stringify(params)
    const signature = signParamsSync(paramsString, authSecret)

    // Uppy expects `{ params: <string|object>, signature: <string> }`.
    return NextResponse.json({ params: paramsString, signature })
  } catch (err) {
    const message = err instanceof Error ? err.message : 'Unknown error'
    return NextResponse.json({ error: message }, { status: 500 })
  }
}

2) 客户端:挂载 Uppy Dashboard 和 Transloadit 插件

添加 CSS(对于 App Router,请在根布局中添加):

import '@uppy/core/css/style.min.css'
import '@uppy/dashboard/css/style.min.css'

创建一个客户端组件,例如 app/upload-demo.tsx:

'use client'

import { useEffect, useMemo, useRef, useState } from 'react'
import Uppy from '@uppy/core'
import Dashboard from '@uppy/dashboard'
import Transloadit, { type AssemblyOptions } from '@uppy/transloadit'

export default function UploadDemo() {
  const dashboardEl = useRef<HTMLDivElement | null>(null)
  const [results, setResults] = useState<unknown>(null)
  const [uploadPct, setUploadPct] = useState<number>(0)

  const uppy = useMemo(() => {
    const instance = new Uppy({
      autoProceed: true,
      restrictions: { maxNumberOfFiles: 1 },
    })

    instance.use(Transloadit, {
      waitForEncoding: true,
      alwaysRunAssembly: true,
      assemblyOptions: async (): Promise<AssemblyOptions> => {
        const res = await fetch('/api/transloadit/assembly-options', { method: 'POST' })
        if (!res.ok) throw new Error(`Failed to get assembly options: ${res.status}`)
        return (await res.json()) as AssemblyOptions
      },
    })

    return instance
  }, [])

  useEffect(() => {
    if (!dashboardEl.current) return

    uppy.use(Dashboard, {
      target: dashboardEl.current,
      inline: true,
      proudlyDisplayPoweredByUppy: false,
      hideUploadButton: true,
      hideProgressDetails: false,
      height: 350,
    })

    const onResult = (stepName: string, result: unknown) =>
      setResults((prev: unknown) => {
        const base: Record<string, unknown> =
          typeof prev === 'object' && prev !== null ? { ...(prev as Record<string, unknown>) } : {}
        const existing = base[stepName]
        base[stepName] = Array.isArray(existing) ? existing.concat([result]) : [result]
        return base
      })

    const onUploadProgress = (_file: unknown, progress: { bytesUploaded: number; bytesTotal: number | null }) => {
      if (!progress?.bytesTotal) return
      setUploadPct(Math.round((progress.bytesUploaded / progress.bytesTotal) * 100))
    }

    uppy.on('transloadit:result', onResult)
    uppy.on('upload-progress', onUploadProgress)

    return () => {
      uppy.off('transloadit:result', onResult)
      uppy.off('upload-progress', onUploadProgress)
      uppy.getPlugin('Dashboard')?.uninstall()
      uppy.destroy()
    }
  }, [uppy])

  return (
    <section>
      <div ref={dashboardEl} />
      <div style={{ marginTop: 12 }}>{uploadPct}%</div>
      <pre style={{ marginTop: 12 }}>{results ? JSON.stringify(results, null, 2) : '(no results yet)'}</pre>
    </section>
  )
}

可选:/s3/store 导出

推荐做法:在 Transloadit 中创建模板凭据(这样就无需在任何地方提供 AWS 密钥),并在 /s3/store 中引用它们。

示例步骤:

{
  "resized": { "robot": "/image/resize", "use": ":original", "width": 320 },
  "exported": {
    "robot": "/s3/store",
    "use": "resized",
    "credentials": "YOUR_TRANSLOADIT_TEMPLATE_CREDENTIALS_NAME",
    "path": "uppy-nextjs/${unique_prefix}/${file.url_name}",
    "acl": "private"
  }
}

如果你有意让对象公开,请将 "acl" 改为 "public-read"(并考虑存储桶策略、访问日志和数据保留)。

然后创建模板并设置 TRANSLOADIT_TEMPLATE_ID:

npx -y @transloadit/node templates create uppy-nextjs-resize-to-s3 ./steps.json -j

参考资料(内部)

  • 可用的参考实现:https://github.com/transloadit/skills/tree/main/scenarios/integrate-uppy-transloadit-s3-uploading-to-nextjs
  • 经验证的步骤 JSON:https://github.com/transloadit/skills/blob/main/scenarios/integrate-uppy-transloadit-s3-uploading-to-nextjs/transloadit/steps/resize-only.json、https://github.com/transloadit/skills/blob/main/scenarios/integrate-uppy-transloadit-s3-uploading-to-nextjs/transloadit/steps/resize-to-s3.json

已测试版本(确切版本请参阅该场景的锁定文件):

  • Next.js 16.1.6(App Router)
  • React 19.2.3
  • @transloadit/utils 4.3.0(Assembly 签名)
  • @uppy/core 5.2.0、@uppy/dashboard 5.1.1、@uppy/transloadit 5.5.0

评论

支持 Markdown