如何将 Cloudflare 的 R2 存储桶与下一个 JS 13 api 一起使用?

问题描述 投票:0回答:2

我最近发现了 Cloudflare 的 R2 存储桶。这个桶有大量的免费套餐,我想将其用于我的个人项目。但没有关于如何在下一个 JS 13 中使用它的文档。可能有一些教程展示如何在 Node js 中使用它。

那么我如何使用 Next 13 API 路由上传到 R2 存储?

reactjs next.js cloudflare cloudflare-workers
2个回答
0
投票

您可以使用 S3 Compatible for R2 来实现您的目标。


0
投票

从我的 reddit 评论中复制此解决方案。我选择了选项 2:直接从浏览器上传到 AWS S3

不敢相信没有人写了有关使用 cloudflare r2 与 next 13 的

app/

 目录的指南,所以我就这样做了 ↓

cloudflare cors 政策

[ { "AllowedOrigins": [ "http://localhost:3000" ], "AllowedMethods": [ "GET", "PUT", "POST", "HEAD", "DELETE" ], "AllowedHeaders": [ "*" ], "ExposeHeaders": [], "MaxAgeSeconds": 3000 } ]
安装这些依赖项

npm 安装@aws-sdk/client-s3 @aws-sdk/s3-request-presigner

.env.local

从 cloudflare r2 仪表板生成令牌。在 Cloudflare Workers YouTube 频道上观看官方 YouTube 视频

R2_ACCESS_KEY_ID=xxxx R2_SECRET_ACCESS_KEY=xxxx R2_BUCKET_NAME=xxxx R2_ACCOUNT_ID=xxxx
lib/r2.ts

import { S3Client } from '@aws-sdk/client-s3' export const r2 = new S3Client({ region: 'auto', endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY_ID || '', secretAccessKey: process.env.R2_SECRET_ACCESS_KEY || '', }, })
应用程序/(站点)/admin/page.tsx

'use client' import React from 'react' import { DocumentIcon } from '@heroicons/react/24/solid' const Admin = () => { const [file, setFile] = React.useState<File>() const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { if (event.target.files) { const currentFile = event.target.files[0] setFile(currentFile) } } const handleUpload = async () => { if (!file) return const formData = new FormData() formData.append('file', file) const response = await fetch('/api/upload', { method: 'POST', }) const { url } = await response.json() await fetch(url, { method: 'PUT', body: formData, }) } return ( <div className="min-h-screen bg-slate-900 text-white space-y-12"> <div className="max-w-2xl mx-auto py-24 px-4"> <h2 className="text-base font-semibold leading-7 text-white"> Admin Panel </h2> <p className="mt-1 text-sm leading-6 text-gray-400"> Upload the latest version of the pdf file. </p> <div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> <div className="col-span-full"> <label htmlFor="pdf-file" className="block text-sm font-medium leading-6 text-white" > PDF </label> <div className="mt-2 flex justify-center rounded-lg border border-dashed border-white/25 px-6 py-10"> <div className="text-center"> <DocumentIcon className="mx-auto h-12 w-12 text-gray-500" aria-hidden="true" /> <div className="mt-4 text-sm leading-6 text-gray-400"> <label htmlFor="file-upload" className="relative cursor-pointer rounded-md bg-gray-900 font-semibold text-white focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-600 focus-within:ring-offset-2 focus-within:ring-offset-gray-900 hover:text-indigo-500" > <span>Upload a file</span> <input type="file" accept="application/pdf" id="file-upload" name="file-upload" className="sr-only" onChange={handleFileChange} /> </label> </div> <p className="text-xs leading-5 text-gray-400"> {file?.name ? file.name : 'PDF up to 100MB'} </p> </div> </div> </div> </div> <div className="mt-6 flex items-center justify-end gap-x-6"> <button type="submit" className="rounded-md bg-indigo-500 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500" onClick={handleUpload} > Upload </button> </div> </div> </div> ) } export default Admin
应用程序/api/upload/route.ts

import { NextResponse } from 'next/server' import chalk from 'chalk' import { PutObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' import { r2 } from '@/lib/r2' export async function POST(request: Request) { try { console.log(chalk.yellow(`Generating an upload URL!`)) const signedUrl = await getSignedUrl( r2, new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME, Key: `filename.pdf`, }), { expiresIn: 60 } ) console.log(chalk.green(`Success generating upload URL!`)) return NextResponse.json({ url: signedUrl }) } catch (err) { console.log('error') } }
此外,还实现了下载:

应用程序/api/download/route.ts

import { GetObjectCommand } from '@aws-sdk/client-s3' import chalk from 'chalk' import { r2 } from '@/lib/r2' export async function GET() { try { console.log(chalk.yellow(`Retrieving pdf from R2!`)) const pdf = await r2.send( new GetObjectCommand({ Bucket: process.env.R2_BUCKET_NAME, Key: 'filename.pdf', }) ) if (!pdf) { throw new Error('pdf not found.') } return new Response(pdf.Body?.transformToWebStream(), { headers: { 'Content-Type': 'application/pdf', }, }) } catch (err) { console.log('error', err) } }
应用程序/(站点)/admin/page.tsx

const handleDownload = async () => { const response = await fetch('/api/download') const blob = await response.blob() const fileURL = window.URL.createObjectURL(blob) let anchor = document.createElement('a') anchor.href = fileURL anchor.download = 'filename.pdf' anchor.click() } // add below upload button <button type="button" className="rounded-md bg-pink-500 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-pink-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-pink-500" onClick={handleDownload} > Download </button>
    
© www.soinside.com 2019 - 2024. All rights reserved.