UploadThing: یک راه حل مدرن آپلود فایل برای برنامه های Next.js

مقدمه
UploadThing یک راه حل آپلود فایل منبع باز است که به طور خاص برای برنامه های Next.js طراحی شده است. این به توسعه دهندگان یک روش ایمن و کارآمد برای مدیریت آپلود فایل ارائه می دهد و در عین حال ویژگی هایی مانند اعتبار سنجی فایل، تبدیل و ادغام مستقیم با چارچوب های محبوب را ارائه می دهد.
بررسی اجمالی فنی
UploadThing در هسته خود از سه جزء اصلی تشکیل شده است:
- روتر فایل سمت سرور
- قطعات و قلاب های سمت مشتری
- نقاط پایانی API ایمن تایپ
نصب و راه اندازی اولیه
ابتدا بسته های مورد نیاز را نصب کنید:
npm install uploadthing @uploadthing/react
یک روتر فایل ایجاد کنید (معمولاً در app/api/uploadthing/core.ts
):
import { createUploadthing, type FileRouter } from "uploadthing/server";
const f = createUploadthing();
export const uploadRouter = {
// Example "profile picture upload" route - these can be named whatever you want!
profilePicture: f(["image"])
.middleware(({ req }) => auth(req))
.onUploadComplete((data) => console.log("file", data)),
// This route takes an attached image OR video
messageAttachment: f(["image", "video"])
.middleware(({ req }) => auth(req))
.onUploadComplete((data) => console.log("file", data)),
// Takes exactly ONE image up to 2MB
strictImageAttachment: f({
image: { maxFileSize: "2MB", maxFileCount: 1, minFileCount: 1 },
})
.middleware(({ req }) => auth(req))
.onUploadComplete((data) => console.log("file", data)),
// Takes up to 4 2mb images and/or 1 256mb video
mediaPost: f({
image: { maxFileSize: "2MB", maxFileCount: 4 },
video: { maxFileSize: "256MB", maxFileCount: 1 },
})
.middleware(({ req }) => auth(req))
.onUploadComplete((data) => console.log("file", data)),
// Takes up to 4 2mb images, and the client will not resolve
// the upload until the `onUploadComplete` resolved.
withAwaitedServerData: f(
{ image: { maxFileSize: "2MB", maxFileCount: 4 } },
{ awaitServerData: true },
)
.middleware(({ req }) => auth(req))
.onUploadComplete((data) => {
return { foo: "bar" as const };
}),
} satisfies FileRouter;
export type UploadRouter = typeof uploadRouter;
اینها مسیرهایی هستند که با کمک راهنما ایجاد می کنید createUploadthing
. آنها را به عنوان “نقاط پایانی” برای آنچه کاربران شما می توانند آپلود کنند، در نظر بگیرید. یک شی با مسیرهای فایل یک مسیریاب فایل می سازد که در آن کلیدها (اسلاگ ها) در شی نام نقاط پایانی شما هستند.
پیکربندی مسیر:
را f
تابع دو آرگومان می گیرد. اولین مورد می تواند آرایه ای از FileType
، یا یک نقشه برداری رکورد هر کدام FileType
با پیکربندی مسیر پیکربندی مسیر به کنترل دقیق تری اجازه می دهد، به عنوان مثال چه فایل هایی را می توان آپلود کرد و چه تعداد از آنها را می توان برای یک آپلود معین آپلود کرد. نحو آرایه به اعمال پیشفرضها برای همه انواع فایلها بازگشته است.
الف FileType
می تواند هر نوع وب MIME معتبر ↗ باشد. به عنوان مثال: استفاده کنید application/json
تا فقط فایل های JSON آپلود شوند.
علاوه بر این، می توانید هر یک از انواع سفارشی زیر را پاس کنید: image
، video
، audio
، pdf
یا text
. اینها کوتاه نویسی هستند که به شما امکان می دهد نوع فایل را بدون تعیین نوع دقیق MIME مشخص کنید. در نهایت، وجود دارد blob
که هر نوع فایلی را اجازه می دهد.
گزینه های مسیر:
استدلال دوم به f
تابع یک شی اختیاری از گزینه های مسیر است. این پیکربندیها تنظیمات کلی را ارائه میکنند که بر نحوه رفتار مسیر آپلود تأثیر میگذارند.
گزینه های مسیر موجود:
-
awaitServerData: (بولی، پیشفرض: نادرست) وقتی روی تنظیم شود
true
، مشتری منتظر سرور خواهد ماندonUploadComplete
کنترل کننده برای اتمام و بازگشت داده ها قبل از اجراonClientUploadComplete
. این زمانی مفید است که قبل از ادامه عملیات سمت سرویس گیرنده باید از تکمیل پردازش سمت سرور اطمینان حاصل کنید.
مثال:
const uploadRouter = {
withServerData: f(
{ image: { maxFileSize: "2MB" } },
{ awaitServerData: true }
)
.middleware(({ req }) => ({ userId: "123" }))
.onUploadComplete(async ({ metadata }) => {
// This will complete before client-side callback
const result = await processImage(metadata);
return { processedUrl: result.url };
}),
}
روش های مسیر
را f
تابع یک شی سازنده را برمی گرداند که به شما امکان می دهد متدهای زیر را زنجیره بزنید:
ورودی
ورودی کاربر از مشتری را با استفاده از اعتبار سنجی طرحواره تأیید می کند. این روش تضمین می کند که هر داده اضافی ارسال شده در کنار آپلود فایل با مشخصات شما مطابقت دارد.
تایید کننده های پشتیبانی شده:
- Zod (≥3)
- اثر/شما (≥3.10، با محدودیت)
- مشخصات طرحواره استاندارد (به عنوان مثال، Valibot ≥1.0، ArkType ≥2.0)
مثال با اعتبارسنجی پیچیده:
f(["image"])
.input(
z.object({
title: z.string().min(1).max(100),
tags: z.array(z.string()),
isPublic: z.boolean()
})
)
.middleware(async ({ req, input }) => {
// input is fully typed with:
// { title: string; tags: string[]; isPublic: boolean }
return { metadata: input };
})
میان افزار
مجوز و برچسب گذاری ابرداده را کنترل می کند. این جایی است که شما بررسی های احراز هویت را انجام می دهید و هر گونه داده مورد نیاز برای فرآیند آپلود را آماده می کنید.
مثال با احراز هویت و فراداده جامع:
f(["image"])
.middleware(async ({ req, res }) => {
const user = await currentUser();
if (!user) throw new UploadThingError("Authentication required");
// You can perform additional checks
const userPlan = await getUserSubscriptionPlan(user.id);
if (userPlan === "free" && await getUserUploadCount(user.id) > 10) {
throw new UploadThingError("Free plan limit reached");
}
return {
userId: user.id,
planType: userPlan,
timestamp: new Date().toISOString()
};
})
خطای onUpload
هنگامی که یک خطای آپلود رخ می دهد تماس گرفته می شود. از این برای رسیدگی به خطاها و انجام هرگونه پاکسازی یا ثبت گزارش لازم استفاده کنید.
پارامترها:
-
error
: UploadThingError (شامل پیام خطا و کد) -
fileKey
: رشته (شناسه منحصر به فرد برای آپلود ناموفق)
مثال:
f(["image"])
.onUploadError(async ({ error, fileKey }) => {
await logger.error("Upload failed", {
error: error.message,
fileKey,
code: error.code
});
await cleanupFailedUpload(fileKey);
})
onUploadComplete
کنترل کننده نهایی برای آپلودهای موفق اینجاست که می توانید فایل آپلود شده را پردازش کرده و هر گونه عملیات لازم پس از آپلود را انجام دهید.
پارامترها:
-
metadata
: داده های ارسال شده از میان افزار -
file
: شی UploadedFileData حاوی:-
name
: نام فایل اصلی -
size
: اندازه فایل بر حسب بایت -
key
: شناسه فایل منحصر به فرد -
url
: URL عمومی فایل آپلود شده
-
مثال با مدیریت جامع:
f(["image"])
.onUploadComplete(async ({ metadata, file }) => {
// Store file reference in database
await db.files.create({
data: {
userId: metadata.userId,
fileName: file.name,
fileSize: file.size,
fileUrl: file.url,
uploadedAt: metadata.timestamp
}
});
// Trigger any post-upload processing
await imageProcessor.optimize(file.url);
// Return data to client if awaitServerData is true
return {
fileId: file.key,
accessUrl: file.url,
processedAt: new Date().toISOString()
};
})
آپلود فایل ها
UploadThing دو روش اصلی برای آپلود فایل ها ارائه می دهد: آپلودهای سمت مشتری و آپلودهای سمت سرور. هر رویکرد مزایای و موارد استفاده خاص خود را دارد.
آپلودهای سمت مشتری
آپلودهای سمت مشتری رویکرد پیشنهادی هستند زیرا چندین مزیت را ارائه می دهند:
- کاهش هزینه های سرور (بدون هزینه ورودی/خروجی)
- انتقال مستقیم فایل به UploadThing
- اعتبار سنجی داخلی و ایمنی نوع
فرآیند به صورت زیر عمل می کند:
- مشتری درخواست آپلود را آغاز می کند
- سرور URL های تعیین شده را تولید می کند
- مشتری مستقیماً در UploadThing بارگذاری می کند
- سرور پس از تکمیل پاسخ تماس دریافت می کند
اجرای مثال با استفاده از React:
import { UploadButton } from "@uploadthing/react";
export default function UploadPage() {
return (
<UploadButton
endpoint="imageUploader"
onClientUploadComplete={(res) => {
console.log("Files: ", res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
alert(`ERROR! ${error.message}`);
}}
/>
);
}
آپلودهای سمت سرور
آپلودهای سمت سرور زمانی مفید هستند که شما نیاز دارید:
- فایلها را قبل از آپلود پردازش یا اعتبارسنجی کنید
- تولید فایل روی سرور
- رسیدگی به الزامات امنیتی خاص
مثال با استفاده از UTApi:
import { UTApi } from "uploadthing/server";
const utapi = new UTApi();
async function uploadServerSideFile(file: File) {
try {
const response = await utapi.uploadFiles(file);
return response.data;
} catch (error) {
console.error("Upload failed:", error);
throw error;
}
}
پیکربندی آپلود
هر دو آپلود مشتری و سرور از گزینه های پیکربندی مختلف پشتیبانی می کنند:
اعتبار سنجی فایل:
const uploadRouter = {
strictImageUpload: f({
image: {
maxFileSize: "4MB",
maxFileCount: 1,
minFileCount: 1
}
})
}
فراداده سفارشی:
f(["image"])
.middleware(({ req }) => ({
userId: req.userId,
uploadedAt: new Date().toISOString()
}))
بارگذاری تماس های برگشتی:
f(["image"])
.onUploadComplete(async ({ metadata, file }) => {
// Handle successful upload
await db.images.create({
data: {
userId: metadata.userId,
url: file.url,
name: file.name
}
});
})
.onUploadError(({ error }) => {
// Handle upload error
console.error("Upload failed:", error);
});
آپلودهای قابل ازسرگیری
UploadThing از آپلودهای مجدد برای فایل های بزرگ پشتیبانی می کند:
const upload = async (file: File, presignedUrl: string) => {
// Get the current range start
const rangeStart = await fetch(presignedUrl, {
method: "HEAD"
}).then((res) =>
parseInt(res.headers.get("x-ut-range-start") ?? "0", 10)
);
// Continue upload from last successful byte
await fetch(presignedUrl, {
method: "PUT",
headers: {
Range: `bytes=${rangeStart}-`,
},
body: file.slice(rangeStart),
});
};
ملاحظات امنیتی
- امضای URL: همه URL های آپلود با کلید API شما امضا شده و دارای مهرهای زمانی انقضا هستند
- اعتبار سنجی فایل: اعتبار سنجی کامل را در مسیرهای فایل خود اجرا کنید
- احراز هویت: همیشه قبل از اجازه آپلود، از میان افزار برای احراز هویت کاربران استفاده کنید
نمونه پیکربندی امن:
const uploadRouter = {
secureUpload: f(["image"])
.middleware(async ({ req }) => {
const user = await auth(req);
if (!user) throw new Error("Unauthorized");
const userQuota = await checkUserQuota(user.id);
if (!userQuota.hasRemaining) {
throw new Error("Upload quota exceeded");
}
return { userId: user.id };
})
.onUploadComplete(async ({ metadata, file }) => {
await saveToDatabase(metadata.userId, file);
await updateUserQuota(metadata.userId);
})
};
کار با فایل ها
پس از آپلود موفقیت آمیز فایل ها در UploadThing، راه های مختلفی برای کار و دسترسی به این فایل ها وجود دارد. در اینجا یک راهنمای جامع در مورد عملیات فایل آورده شده است.
دسترسی به فایل های عمومی
فایلها از طریق CDN UploadThing با استفاده از الگوی URL زیر ارائه میشوند:
https://.ufs.sh/f/
اگر یک را تنظیم کرده اید customId
در حین آپلود، می توانید با استفاده از:
https://.ufs.sh/f/
مهم است: هرگز از URL های ارائه دهنده ذخیره سازی خام استفاده نکنید (به عنوان مثال،
https://bucket.s3.region.amazonaws.com/
). UploadThing ممکن است ارائهدهندگان یا سطلهای ذخیرهسازی را تغییر دهد و این URLها را غیرقابل اعتماد کند.
راه اندازی بهینه سازی تصویر (مثال Next.js)
/** @type {import('next').NextConfig} */
export default {
images: {
remotePatterns: [
{
protocol: "https",
hostname: ".ufs.sh ",
pathname: "/f/*",
},
],
},
};
مرجع UTApi
UploadThing API Helper برای استفاده در سمت سرور طراحی شده است. در حالی که یک رابط REST API را ارائه می دهد، عملکرد و ایمنی نوع پیشرفته ای را ارائه می دهد.
توجه: تماسهای API خارجی معمولاً کندتر از پرسوجو از پایگاه داده شما هستند. توصیه می شود داده های فایل لازم را در پایگاه داده خود ذخیره کنید
onUploadComplete()
یا بعد از استفادهuploadFiles()
، به جای تکیه بر API برای جریان داده اصلی.
سازنده
مقداردهی اولیه یک نمونه از UTApi
:
import { UTApi } from "uploadthing/server";
export const utapi = new UTApi({
// ...options
});
گزینه های پیکربندی:
- واکشی: تابع واکشی سفارشی
- نشانه: نشانه UploadThing شما (پیشفرض: env.UPLOADTHING_TOKEN)
- logLevel: پرحرفی گزارش (خطا | هشدار | اطلاعات | اشکال زدایی | ردیابی)
- logFormat: فرمت لاگ (json | logFmt | ساختاریافته | زیبا)
- defaultKeyType: نوع کلید پیش فرض برای عملیات فایل ('fileKey' | 'customId')
- apiUrl: URL API UploadThing (پیشفرض: https://api.uploadthing.com)
- ingestUrl: UploadThing Ingest URL API
عملیات فایل
آپلود فایل ها
import { utapi } from "~/server/uploadthing";
async function uploadFiles(formData: FormData) {
"use server";
const files = formData.getAll("files");
const response = await utapi.uploadFiles(files);
}
آپلود فایل ها از URL
const fileUrl = "https://test.com/some.png";
const uploadedFile = await utapi.uploadFilesFromUrl(fileUrl);
const fileUrls = ["https://test.com/some.png", "https://test.com/some2.png"];
const uploadedFiles = await utapi.uploadFilesFromUrl(fileUrls);
حذف فایل ها
await utapi.deleteFiles("2e0fdb64-9957-4262-8e45-f372ba903ac8_image.jpg");
await utapi.deleteFiles([
"2e0fdb64-9957-4262-8e45-f372ba903ac8_image.jpg",
"1649353b-04ea-48a2-9db7-31de7f562c8d_image2.jpg",
]);
لیست فایل ها
const files = await utapi.listFiles({
limit: 500, // optional, default: 500
offset: 0 // optional, default: 0
});
تغییر نام فایل ها
await utapi.renameFiles({
key: "2e0fdb64-9957-4262-8e45-f372ba903ac8_image.jpg",
newName: "myImage.jpg",
});
// Batch rename
await utapi.renameFiles([
{
key: "2e0fdb64-9957-4262-8e45-f372ba903ac8_image.jpg",
newName: "myImage.jpg",
},
{
key: "1649353b-04ea-48a2-9db7-31de7f562c8d_image2.jpg",
newName: "myOtherImage.jpg",
},
]);
دریافت URL امضا شده
const fileKey = "2e0fdb64-9957-4262-8e45-f372ba903ac8_image.jpg";
const url = await utapi.getSignedURL(fileKey, {
expiresIn: 60 * 60, // 1 hour
// or use time strings:
// expiresIn: '1 hour',
// expiresIn: '3d',
// expiresIn: '7 days',
});
ACL را به روز کنید
// Make a single file public
await utapi.updateACL(
"2e0fdb64-9957-4262-8e45-f372ba903ac8_image.jpg",
"public-read"
);
// Make multiple files private
await utapi.updateACL(
[
"2e0fdb64-9957-4262-8e45-f372ba903ac8_image.jpg",
"1649353b-04ea-48a2-9db7-31de7f562c8d_image2.jpg",
],
"private"
);
دسترسی به فایل های خصوصی
برای فایلهایی که با کنترلهای دسترسی محافظت میشوند، باید URLهای تعیینشده کوتاه مدت ایجاد کنید. دو راه برای این کار وجود دارد:
با استفاده از UTApi:
import { UTApi } from "uploadthing/server";
const utapi = new UTApi();
async function getFileAccess(fileKey: string) {
const signedUrl = await utapi.getSignedUrl(fileKey, {
expiresIn: "1h" // Optional expiration time
});
return signedUrl;
}
با استفاده از REST API Endpoint:
async function requestFileAccess(fileKey: string) {
const response = await fetch("/api/requestFileAccess", {
method: "POST",
body: JSON.stringify({ fileKey })
});
const { signedUrl } = await response.json();
return signedUrl;
}
بهترین شیوه ها
مدیریت URL:
- همیشه از URL های CDN ارائه شده توسط UploadThing استفاده کنید
- کلیدهای فایل را به جای URL های کامل در پایگاه داده خود ذخیره کنید
- URL های تعیین شده را بر اساس درخواست برای فایل های خصوصی ایجاد کنید
امنیت:
- کنترل های دسترسی مناسب را در میان افزار خود پیاده کنید
- از زمان انقضای کوتاه برای URL های تعیین شده استفاده کنید
- قبل از ایجاد URLهای امضا شده، مجوزهای دسترسی به فایل را تأیید کنید
عملکرد:
- از CDN برای تحویل بهینه فایل استفاده کنید
- اجرای کش برای فایل هایی که اغلب به آنها دسترسی دارند را در نظر بگیرید
- از تنظیمات مناسب بهینه سازی تصویر استفاده کنید
اجرای نمونه ای که این شیوه ها را ترکیب می کند:
const fileManager = {
async getFileUrl(fileKey: string, userId: string) {
// Check user permissions
const hasAccess = await checkUserFileAccess(userId, fileKey);
if (!hasAccess) {
throw new Error("Unauthorized access");
}
// Get cached URL if available
const cachedUrl = await cache.get(`file:${fileKey}`);
if (cachedUrl) return cachedUrl;
// Generate new signed URL
const signedUrl = await utapi.getSignedUrl(fileKey, {
expiresIn: "1h"
});
// Cache the URL (for slightly less than expiration time)
await cache.set(`file:${fileKey}`, signedUrl, 50 * 60); // 50 minutes
return signedUrl;
},
async deleteUserFile(fileKey: string, userId: string) {
// Verify ownership
const isOwner = await verifyFileOwnership(userId, fileKey);
if (!isOwner) {
throw new Error("Unauthorized deletion");
}
// Delete file
await utapi.deleteFiles(fileKey);
// Clean up database records
await db.files.delete({
where: { fileKey }
});
// Clear cache
await cache.del(`file:${fileKey}`);
}
};
نتیجه گیری
UploadThing یک راه حل قوی و ایمن برای مدیریت آپلود فایل در برنامه های Next.js ارائه می دهد. نقاط قوت اصلی آن عبارتند از:
- تجربه توسعه دهنده: API های ایمن نوع و ادغام بصری با اجزای React
- انعطاف پذیری: پشتیبانی از آپلودهای سمت سرویس گیرنده و سرور با گردش کار قابل تنظیم
- امنیت: اعتبار سنجی فایل داخلی، کنترل های دسترسی، و امضای امن URL
- عملکرد: تحویل با پشتوانه CDN و بارگذاری مجدد برای فایل های بزرگ
چه در حال ساخت یک ویژگی آپلود تصویر ساده یا یک سیستم مدیریت فایل پیچیده باشید، UploadThing ابزارها و انعطافپذیری لازم برای پیادهسازی مدیریت امن و کارآمد فایل در برنامههای شما را ارائه میدهد.
برای اطلاعات بیشتر و بهروزرسانیها، از اسناد رسمی UploadThing دیدن کنید.