文件存储
Use persistent and ephemeral file storage
Edge Functions 提供两种文件存储方式:
🌐 Edge Functions provides two flavors of file storage:
- 持久 - 支持 S3 协议,可以从任何兼容 S3 的存储桶读写,包括 Supabase Storage
- 短暂 - 你可以读写
/tmp目录下的文件。只适合临时操作
你可以使用文件存储来:
🌐 You can use file storage to:
- 处理复杂的文件转换和工作流程
- 在项目之间进行数据迁移
- 处理用户上传的文件并保存它们
- 解压档案并处理内容后再保存到数据库
持久存储 #
🌐 Persistent Storage
持久存储选项是建立在 S3 协议之上的。它允许你将任何兼容 S3 的存储桶,包括 Supabase 存储桶,挂载为 Edge Functions 的目录。你可以像在 POSIX 文件系统中一样,对挂载的存储桶进行读取和写入文件的操作。
🌐 The persistent storage option is built on top of the S3 protocol. It allows you to mount any S3-compatible bucket, including Supabase Storage Buckets, as a directory for your Edge Functions. You can perform operations such as reading and writing files to the mounted buckets as you would in a POSIX file system.
要从 Edge Functions 访问 S3 存储桶,你必须在 Edge Function Secrets 中为环境变量设置以下内容。
🌐 To access an S3 bucket from Edge Functions, you must set the following for environment variables in Edge Function Secrets.
S3FS_ENDPOINT_URLS3FS_REGIONS3FS_ACCESS_KEY_IDS3FS_SECRET_ACCESS_KEY
按照此指南 启用并为 Supabase Storage S3 创建访问密钥。
要从你的 Edge Function 访问已挂载桶中的文件路径,请使用前缀 /s3/YOUR-BUCKET-NAME。
🌐 To access a file path in your mounted bucket from your Edge Function, use the prefix /s3/YOUR-BUCKET-NAME.
// read from S3 bucketconst data = await Deno.readFile('/s3/my-bucket/results.csv')// make a directoryawait Deno.mkdir('/s3/my-bucket/sub-dir')// write to S3 bucketawait Deno.writeTextFile('/s3/my-bucket/demo.txt', 'hello world')临时存储 #
🌐 Ephemeral storage
临时存储在每次函数调用时都会重置。这意味着你在一次调用中写入的文件只能在同一次调用中读取。
🌐 Ephemeral storage will reset on each function invocation. This means the files you write during an invocation can only be read within the same invocation.
你可以使用 Deno 文件系统 API 或 node:fs 模块来访问 /tmp 路径。
🌐 You can use Deno File System APIs or the node:fs module to access the /tmp path.
Deno.serve(async (req) => { if (req.headers.get('content-type') !== 'application/zip') { return new Response('file must be a zip file', { status: 400, }) } const uploadId = crypto.randomUUID() await Deno.writeFile('/tmp/' + uploadId, req.body) // E.g. extract and process the zip file const zipFile = await Deno.readFile('/tmp/' + uploadId) // You could use a zip library to extract contents const extracted = await extractZip(zipFile) // Or process the file directly console.log(`Processing zip file: ${uploadId}, size: ${zipFile.length} bytes`)})常见用例 #
🌐 Common use cases
使用后台任务处理归档 #
🌐 Archive processing with background tasks
你可以使用临时存储和 后台任务 来处理超过内存限制的大文件处理操作。
🌐 You can use ephemeral storage with Background Tasks to handle large file processing operations that exceed memory limits.
想象一下,你有一个照片相册应用,它接受以 zip 文件形式上传的照片。使用流式实现的话,当 zip 文件超过 100MB 时会遇到内存限制错误,因为它会把所有归档文件同时保存在内存中。
🌐 Imagine you have a Photo Album application that accepts photo uploads as zip files. A streaming implementation will run into memory limit errors with zip files exceeding 100MB, as it retains all archive files in memory simultaneously.
你可以先把 zip 文件写入临时存储,然后用后台任务提取并上传文件到 Supabase 存储。这样,你只需要将 zip 文件的部分内容读入内存。
🌐 You can write the zip file to ephemeral storage first, then use a background task to extract and upload files to Supabase Storage. This way, you only read parts of the zip file to the memory.
import { BlobWriter, ZipReader } from 'https://deno.land/x/zipjs/index.js'import { createClient } from 'jsr:@supabase/supabase-js@2'const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)// If you want to use a different api key, change 'default' to your preferred key nameconst supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default'])async function processZipFile(uploadId: string, filepath: string) { const file = await Deno.open(filepath, { read: true }) const zipReader = new ZipReader(file.readable) const entries = await zipReader.getEntries() await supabase.storage.createBucket(uploadId, { public: false }) await Promise.all( entries.map(async (entry) => { if (entry.directory) return // Read file entry from temp storage const blobWriter = new BlobWriter() const blob = await entry.getData(blobWriter) // Upload to permanent storage await supabase.storage.from(uploadId).upload(entry.filename, blob) console.log('uploaded', entry.filename) }) ) await zipReader.close()}Deno.serve(async (req) => { const uploadId = crypto.randomUUID() const filepath = `/tmp/${uploadId}.zip` // Write zip to ephemeral storage await Deno.writeFile(filepath, req.body) // Process in background to avoid memory limits EdgeRuntime.waitUntil(processZipFile(uploadId, filepath)) return new Response(JSON.stringify({ uploadId }), { headers: { 'Content-Type': 'application/json' }, })})图片处理 #
🌐 Image manipulation
使用 magick-wasm 的自定义图片处理工作流程。
🌐 Custom image manipulation workflows using magick-wasm.
Deno.serve(async (req) => { // Save uploaded image to temp storage const imagePath = `/tmp/input-${crypto.randomUUID()}.jpg` await Deno.writeFile(imagePath, req.body) // Process image with magick-wasm const processedPath = `/tmp/output-${crypto.randomUUID()}.jpg` // ... image manipulation logic // Read processed image and return const processedImage = await Deno.readFile(processedPath) return new Response(processedImage, { headers: { 'Content-Type': 'image/jpeg' }, })})使用同步文件 API #
🌐 Using synchronous file APIs
你可以在初始脚本评估期间安全地使用以下同步 Deno API(以及它们的 Node 对应 API):
🌐 You can safely use the following synchronous Deno APIs (and their Node counterparts) during initial script evaluation:
- Deno.statSync
- Deno.removeSync
- Deno.writeFileSync
- Deno.writeTextFileSync
- Deno.readFileSync
- Deno.readTextFileSync
- Deno.mkdirSync
- Deno.makeTempDirSync
- Deno.readDirSync
记住,同步 API 只在初始脚本评估时可用,并且不支持在回调中使用,例如 HTTP 处理器或 setTimeout。
Deno.statSync('...') // ✅setTimeout(() => { Deno.statSync('...') // 💣 ERROR! Deno.statSync is blocklisted on the current context})Deno.serve(() => { Deno.statSync('...') // 💣 ERROR! Deno.statSync is blocklisted on the current context})限制 #
🌐 Limits
你挂载用于持久存储的 S3 桶没有数量限制。
🌐 There are no limits on S3 buckets you mount for Persistent storage.
临时存储:
🌐 Ephemeral Storage:
- 免费项目:最多 256MB 临时存储
- 付费项目:最多512MB的临时存储