_250import type { APIRoute } from "astro";_250import { API } from "../../utils/api";_250_250interface MultipartUploadRequest {_250 key: string;_250 contentType?: string;_250}_250_250interface CompleteMultipartRequest {_250 uploadId: string;_250 key: string;_250 parts: R2UploadedPart[];_250}_250_250// Creates and completes a new multipart upload session_250export const POST: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (!action) {_250 return API.error("Missing action parameter", request, 400);_250 }_250_250 switch (action) {_250 case "create": {_250 // Create a new multipart upload_250 const body: MultipartUploadRequest = await request.json();_250_250 if (!body.key) {_250 return API.error("Missing key parameter", request, 400);_250 }_250_250 try {_250 const multipartUpload = await bucket.createMultipartUpload(body.key, {_250 httpMetadata: body.contentType_250 ? {_250 contentType: body.contentType,_250 }_250 : undefined,_250 });_250_250 return API.success(_250 {_250 success: true,_250 key: multipartUpload.key,_250 uploadId: multipartUpload.uploadId,_250 },_250 request_250 );_250 } catch (error) {_250 console.error("Failed to create multipart upload:", error);_250 return API.error("Failed to create multipart upload", request, 500);_250 }_250 }_250_250 case "complete": {_250 // Complete a multipart upload_250 const body: CompleteMultipartRequest = await request.json();_250_250 if (!body.uploadId || !body.key || !body.parts) {_250 return API.error("Missing required parameters", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(_250 body.key,_250 body.uploadId_250 );_250_250 // Parts are already in R2UploadedPart format_250 const r2Parts = body.parts;_250_250 const object = await multipartUpload.complete(r2Parts);_250_250 return API.success(_250 {_250 success: true,_250 key: object.key,_250 etag: object.httpEtag,_250 size: object.size,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to complete multipart upload:", error);_250 return API.error(_250 error.message || "Failed to complete multipart upload",_250 request,_250 400_250 );_250 }_250 }_250_250 default:_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250 } catch (error) {_250 console.error("Multipart upload error:", error);_250 return API.error("Multipart upload failed", request, 500);_250 }_250};_250_250// Uploads individual parts of a multipart upload_250export const PUT: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "upload-part") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const partNumberStr = url.searchParams.get("partNumber");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !partNumberStr || !key) {_250 return API.error("Missing uploadId, partNumber, or key", request, 400);_250 }_250_250 const partNumber = parseInt(partNumberStr);_250 if (isNaN(partNumber) || partNumber < 1) {_250 return API.error("Invalid part number", request, 400);_250 }_250_250 if (!request.body) {_250 return API.error("Missing request body", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250_250 // Convert request body to ArrayBuffer to get known length_250 const arrayBuffer = await request.arrayBuffer();_250 const uploadedPart = await multipartUpload.uploadPart(_250 partNumber,_250 arrayBuffer_250 );_250_250 return API.success(_250 {_250 success: true,_250 partNumber: uploadedPart.partNumber,_250 etag: uploadedPart.etag,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to upload part:", error);_250 return API.error(error.message || "Failed to upload part", request, 400);_250 }_250 } catch (error) {_250 console.error("Upload part error:", error);_250 return API.error("Upload part failed", request, 500);_250 }_250};_250_250// Aborts a multipart upload_250export const DELETE: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "abort") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !key) {_250 return API.error("Missing uploadId or key", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250 await multipartUpload.abort();_250_250 return API.success(_250 {_250 success: true,_250 message: "Multipart upload aborted successfully",_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to abort multipart upload:", error);_250 return API.error(_250 error.message || "Failed to abort multipart upload",_250 request,_250 400_250 );_250 }_250 } catch (error) {_250 console.error("Abort multipart upload error:", error);_250 return API.error("Abort multipart upload failed", request, 500);_250 }_250};_250_250export const OPTIONS: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250 return API.cors(request);_250};
Create the POST Endpoint
The POST endpoint manages multipart uploads through two key operations: initializing new uploads and finalizing completed ones. The action query parameter determines which operation to perform.
Be sure to include the locals parameter in the POST endpoint to access your environment variables and CLOUD_FILES binding.
Setup CORS handling
_250import type { APIRoute } from "astro";_250import { API } from "../../utils/api";_250_250interface MultipartUploadRequest {_250 key: string;_250 contentType?: string;_250}_250_250interface CompleteMultipartRequest {_250 uploadId: string;_250 key: string;_250 parts: R2UploadedPart[];_250}_250_250// Creates and completes a new multipart upload session_250export const POST: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (!action) {_250 return API.error("Missing action parameter", request, 400);_250 }_250_250 switch (action) {_250 case "create": {_250 // Create a new multipart upload_250 const body: MultipartUploadRequest = await request.json();_250_250 if (!body.key) {_250 return API.error("Missing key parameter", request, 400);_250 }_250_250 try {_250 const multipartUpload = await bucket.createMultipartUpload(body.key, {_250 httpMetadata: body.contentType_250 ? {_250 contentType: body.contentType,_250 }_250 : undefined,_250 });_250_250 return API.success(_250 {_250 success: true,_250 key: multipartUpload.key,_250 uploadId: multipartUpload.uploadId,_250 },_250 request_250 );_250 } catch (error) {_250 console.error("Failed to create multipart upload:", error);_250 return API.error("Failed to create multipart upload", request, 500);_250 }_250 }_250_250 case "complete": {_250 // Complete a multipart upload_250 const body: CompleteMultipartRequest = await request.json();_250_250 if (!body.uploadId || !body.key || !body.parts) {_250 return API.error("Missing required parameters", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(_250 body.key,_250 body.uploadId_250 );_250_250 // Parts are already in R2UploadedPart format_250 const r2Parts = body.parts;_250_250 const object = await multipartUpload.complete(r2Parts);_250_250 return API.success(_250 {_250 success: true,_250 key: object.key,_250 etag: object.httpEtag,_250 size: object.size,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to complete multipart upload:", error);_250 return API.error(_250 error.message || "Failed to complete multipart upload",_250 request,_250 400_250 );_250 }_250 }_250_250 default:_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250 } catch (error) {_250 console.error("Multipart upload error:", error);_250 return API.error("Multipart upload failed", request, 500);_250 }_250};_250_250// Uploads individual parts of a multipart upload_250export const PUT: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "upload-part") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const partNumberStr = url.searchParams.get("partNumber");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !partNumberStr || !key) {_250 return API.error("Missing uploadId, partNumber, or key", request, 400);_250 }_250_250 const partNumber = parseInt(partNumberStr);_250 if (isNaN(partNumber) || partNumber < 1) {_250 return API.error("Invalid part number", request, 400);_250 }_250_250 if (!request.body) {_250 return API.error("Missing request body", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250_250 // Convert request body to ArrayBuffer to get known length_250 const arrayBuffer = await request.arrayBuffer();_250 const uploadedPart = await multipartUpload.uploadPart(_250 partNumber,_250 arrayBuffer_250 );_250_250 return API.success(_250 {_250 success: true,_250 partNumber: uploadedPart.partNumber,_250 etag: uploadedPart.etag,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to upload part:", error);_250 return API.error(error.message || "Failed to upload part", request, 400);_250 }_250 } catch (error) {_250 console.error("Upload part error:", error);_250 return API.error("Upload part failed", request, 500);_250 }_250};_250_250// Aborts a multipart upload_250export const DELETE: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "abort") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !key) {_250 return API.error("Missing uploadId or key", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250 await multipartUpload.abort();_250_250 return API.success(_250 {_250 success: true,_250 message: "Multipart upload aborted successfully",_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to abort multipart upload:", error);_250 return API.error(_250 error.message || "Failed to abort multipart upload",_250 request,_250 400_250 );_250 }_250 } catch (error) {_250 console.error("Abort multipart upload error:", error);_250 return API.error("Abort multipart upload failed", request, 500);_250 }_250};_250_250export const OPTIONS: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250 return API.cors(request);_250};
Since multipart uploads require direct communication with the worker domain (not your Webflow Cloud domain), you need to set up proper CORS configuration. The imported API utility automatically handles CORS headers and preflight requests to ensure seamless cross-origin communication.
Access your Object Storage bucket
_250import type { APIRoute } from "astro";_250import { API } from "../../utils/api";_250_250interface MultipartUploadRequest {_250 key: string;_250 contentType?: string;_250}_250_250interface CompleteMultipartRequest {_250 uploadId: string;_250 key: string;_250 parts: R2UploadedPart[];_250}_250_250// Creates and completes a new multipart upload session_250export const POST: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (!action) {_250 return API.error("Missing action parameter", request, 400);_250 }_250_250 switch (action) {_250 case "create": {_250 // Create a new multipart upload_250 const body: MultipartUploadRequest = await request.json();_250_250 if (!body.key) {_250 return API.error("Missing key parameter", request, 400);_250 }_250_250 try {_250 const multipartUpload = await bucket.createMultipartUpload(body.key, {_250 httpMetadata: body.contentType_250 ? {_250 contentType: body.contentType,_250 }_250 : undefined,_250 });_250_250 return API.success(_250 {_250 success: true,_250 key: multipartUpload.key,_250 uploadId: multipartUpload.uploadId,_250 },_250 request_250 );_250 } catch (error) {_250 console.error("Failed to create multipart upload:", error);_250 return API.error("Failed to create multipart upload", request, 500);_250 }_250 }_250_250 case "complete": {_250 // Complete a multipart upload_250 const body: CompleteMultipartRequest = await request.json();_250_250 if (!body.uploadId || !body.key || !body.parts) {_250 return API.error("Missing required parameters", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(_250 body.key,_250 body.uploadId_250 );_250_250 // Parts are already in R2UploadedPart format_250 const r2Parts = body.parts;_250_250 const object = await multipartUpload.complete(r2Parts);_250_250 return API.success(_250 {_250 success: true,_250 key: object.key,_250 etag: object.httpEtag,_250 size: object.size,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to complete multipart upload:", error);_250 return API.error(_250 error.message || "Failed to complete multipart upload",_250 request,_250 400_250 );_250 }_250 }_250_250 default:_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250 } catch (error) {_250 console.error("Multipart upload error:", error);_250 return API.error("Multipart upload failed", request, 500);_250 }_250};_250_250// Uploads individual parts of a multipart upload_250export const PUT: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "upload-part") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const partNumberStr = url.searchParams.get("partNumber");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !partNumberStr || !key) {_250 return API.error("Missing uploadId, partNumber, or key", request, 400);_250 }_250_250 const partNumber = parseInt(partNumberStr);_250 if (isNaN(partNumber) || partNumber < 1) {_250 return API.error("Invalid part number", request, 400);_250 }_250_250 if (!request.body) {_250 return API.error("Missing request body", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250_250 // Convert request body to ArrayBuffer to get known length_250 const arrayBuffer = await request.arrayBuffer();_250 const uploadedPart = await multipartUpload.uploadPart(_250 partNumber,_250 arrayBuffer_250 );_250_250 return API.success(_250 {_250 success: true,_250 partNumber: uploadedPart.partNumber,_250 etag: uploadedPart.etag,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to upload part:", error);_250 return API.error(error.message || "Failed to upload part", request, 400);_250 }_250 } catch (error) {_250 console.error("Upload part error:", error);_250 return API.error("Upload part failed", request, 500);_250 }_250};_250_250// Aborts a multipart upload_250export const DELETE: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "abort") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !key) {_250 return API.error("Missing uploadId or key", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250 await multipartUpload.abort();_250_250 return API.success(_250 {_250 success: true,_250 message: "Multipart upload aborted successfully",_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to abort multipart upload:", error);_250 return API.error(_250 error.message || "Failed to abort multipart upload",_250 request,_250 400_250 );_250 }_250 } catch (error) {_250 console.error("Abort multipart upload error:", error);_250 return API.error("Abort multipart upload failed", request, 500);_250 }_250};_250_250export const OPTIONS: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250 return API.cors(request);_250};
Retrieve the CLOUD_FILES binding that provides access to your Object Storage bucket. This binding contains all the multipart upload methods you'll need throughout the upload process.
Get Action Parameter
_250import type { APIRoute } from "astro";_250import { API } from "../../utils/api";_250_250interface MultipartUploadRequest {_250 key: string;_250 contentType?: string;_250}_250_250interface CompleteMultipartRequest {_250 uploadId: string;_250 key: string;_250 parts: R2UploadedPart[];_250}_250_250// Creates and completes a new multipart upload session_250export const POST: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (!action) {_250 return API.error("Missing action parameter", request, 400);_250 }_250_250 switch (action) {_250 case "create": {_250 // Create a new multipart upload_250 const body: MultipartUploadRequest = await request.json();_250_250 if (!body.key) {_250 return API.error("Missing key parameter", request, 400);_250 }_250_250 try {_250 const multipartUpload = await bucket.createMultipartUpload(body.key, {_250 httpMetadata: body.contentType_250 ? {_250 contentType: body.contentType,_250 }_250 : undefined,_250 });_250_250 return API.success(_250 {_250 success: true,_250 key: multipartUpload.key,_250 uploadId: multipartUpload.uploadId,_250 },_250 request_250 );_250 } catch (error) {_250 console.error("Failed to create multipart upload:", error);_250 return API.error("Failed to create multipart upload", request, 500);_250 }_250 }_250_250 case "complete": {_250 // Complete a multipart upload_250 const body: CompleteMultipartRequest = await request.json();_250_250 if (!body.uploadId || !body.key || !body.parts) {_250 return API.error("Missing required parameters", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(_250 body.key,_250 body.uploadId_250 );_250_250 // Parts are already in R2UploadedPart format_250 const r2Parts = body.parts;_250_250 const object = await multipartUpload.complete(r2Parts);_250_250 return API.success(_250 {_250 success: true,_250 key: object.key,_250 etag: object.httpEtag,_250 size: object.size,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to complete multipart upload:", error);_250 return API.error(_250 error.message || "Failed to complete multipart upload",_250 request,_250 400_250 );_250 }_250 }_250_250 default:_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250 } catch (error) {_250 console.error("Multipart upload error:", error);_250 return API.error("Multipart upload failed", request, 500);_250 }_250};_250_250// Uploads individual parts of a multipart upload_250export const PUT: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "upload-part") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const partNumberStr = url.searchParams.get("partNumber");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !partNumberStr || !key) {_250 return API.error("Missing uploadId, partNumber, or key", request, 400);_250 }_250_250 const partNumber = parseInt(partNumberStr);_250 if (isNaN(partNumber) || partNumber < 1) {_250 return API.error("Invalid part number", request, 400);_250 }_250_250 if (!request.body) {_250 return API.error("Missing request body", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250_250 // Convert request body to ArrayBuffer to get known length_250 const arrayBuffer = await request.arrayBuffer();_250 const uploadedPart = await multipartUpload.uploadPart(_250 partNumber,_250 arrayBuffer_250 );_250_250 return API.success(_250 {_250 success: true,_250 partNumber: uploadedPart.partNumber,_250 etag: uploadedPart.etag,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to upload part:", error);_250 return API.error(error.message || "Failed to upload part", request, 400);_250 }_250 } catch (error) {_250 console.error("Upload part error:", error);_250 return API.error("Upload part failed", request, 500);_250 }_250};_250_250// Aborts a multipart upload_250export const DELETE: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "abort") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !key) {_250 return API.error("Missing uploadId or key", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250 await multipartUpload.abort();_250_250 return API.success(_250 {_250 success: true,_250 message: "Multipart upload aborted successfully",_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to abort multipart upload:", error);_250 return API.error(_250 error.message || "Failed to abort multipart upload",_250 request,_250 400_250 );_250 }_250 } catch (error) {_250 console.error("Abort multipart upload error:", error);_250 return API.error("Abort multipart upload failed", request, 500);_250 }_250};_250_250export const OPTIONS: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250 return API.cors(request);_250};
Parse the action query parameter to route between different multipart upload operations. The POST endpoint supports both create and complete actions.
Initialize multipart upload session
_250import type { APIRoute } from "astro";_250import { API } from "../../utils/api";_250_250interface MultipartUploadRequest {_250 key: string;_250 contentType?: string;_250}_250_250interface CompleteMultipartRequest {_250 uploadId: string;_250 key: string;_250 parts: R2UploadedPart[];_250}_250_250// Creates and completes a new multipart upload session_250export const POST: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (!action) {_250 return API.error("Missing action parameter", request, 400);_250 }_250_250 switch (action) {_250 case "create": {_250 // Create a new multipart upload_250 const body: MultipartUploadRequest = await request.json();_250_250 if (!body.key) {_250 return API.error("Missing key parameter", request, 400);_250 }_250_250 try {_250 const multipartUpload = await bucket.createMultipartUpload(body.key, {_250 httpMetadata: body.contentType_250 ? {_250 contentType: body.contentType,_250 }_250 : undefined,_250 });_250_250 return API.success(_250 {_250 success: true,_250 key: multipartUpload.key,_250 uploadId: multipartUpload.uploadId,_250 },_250 request_250 );_250 } catch (error) {_250 console.error("Failed to create multipart upload:", error);_250 return API.error("Failed to create multipart upload", request, 500);_250 }_250 }_250_250 case "complete": {_250 // Complete a multipart upload_250 const body: CompleteMultipartRequest = await request.json();_250_250 if (!body.uploadId || !body.key || !body.parts) {_250 return API.error("Missing required parameters", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(_250 body.key,_250 body.uploadId_250 );_250_250 // Parts are already in R2UploadedPart format_250 const r2Parts = body.parts;_250_250 const object = await multipartUpload.complete(r2Parts);_250_250 return API.success(_250 {_250 success: true,_250 key: object.key,_250 etag: object.httpEtag,_250 size: object.size,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to complete multipart upload:", error);_250 return API.error(_250 error.message || "Failed to complete multipart upload",_250 request,_250 400_250 );_250 }_250 }_250_250 default:_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250 } catch (error) {_250 console.error("Multipart upload error:", error);_250 return API.error("Multipart upload failed", request, 500);_250 }_250};_250_250// Uploads individual parts of a multipart upload_250export const PUT: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "upload-part") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const partNumberStr = url.searchParams.get("partNumber");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !partNumberStr || !key) {_250 return API.error("Missing uploadId, partNumber, or key", request, 400);_250 }_250_250 const partNumber = parseInt(partNumberStr);_250 if (isNaN(partNumber) || partNumber < 1) {_250 return API.error("Invalid part number", request, 400);_250 }_250_250 if (!request.body) {_250 return API.error("Missing request body", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250_250 // Convert request body to ArrayBuffer to get known length_250 const arrayBuffer = await request.arrayBuffer();_250 const uploadedPart = await multipartUpload.uploadPart(_250 partNumber,_250 arrayBuffer_250 );_250_250 return API.success(_250 {_250 success: true,_250 partNumber: uploadedPart.partNumber,_250 etag: uploadedPart.etag,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to upload part:", error);_250 return API.error(error.message || "Failed to upload part", request, 400);_250 }_250 } catch (error) {_250 console.error("Upload part error:", error);_250 return API.error("Upload part failed", request, 500);_250 }_250};_250_250// Aborts a multipart upload_250export const DELETE: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "abort") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !key) {_250 return API.error("Missing uploadId or key", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250 await multipartUpload.abort();_250_250 return API.success(_250 {_250 success: true,_250 message: "Multipart upload aborted successfully",_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to abort multipart upload:", error);_250 return API.error(_250 error.message || "Failed to abort multipart upload",_250 request,_250 400_250 );_250 }_250 } catch (error) {_250 console.error("Abort multipart upload error:", error);_250 return API.error("Abort multipart upload failed", request, 500);_250 }_250};_250_250export const OPTIONS: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250 return API.cors(request);_250};
When action=create, this code establishes a new multipart upload session in Object Storage. The browser provides the key and contentType, then receives a unique uploadId that identifies the entire upload process.
Finalize multipart upload
_250import type { APIRoute } from "astro";_250import { API } from "../../utils/api";_250_250interface MultipartUploadRequest {_250 key: string;_250 contentType?: string;_250}_250_250interface CompleteMultipartRequest {_250 uploadId: string;_250 key: string;_250 parts: R2UploadedPart[];_250}_250_250// Creates and completes a new multipart upload session_250export const POST: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (!action) {_250 return API.error("Missing action parameter", request, 400);_250 }_250_250 switch (action) {_250 case "create": {_250 // Create a new multipart upload_250 const body: MultipartUploadRequest = await request.json();_250_250 if (!body.key) {_250 return API.error("Missing key parameter", request, 400);_250 }_250_250 try {_250 const multipartUpload = await bucket.createMultipartUpload(body.key, {_250 httpMetadata: body.contentType_250 ? {_250 contentType: body.contentType,_250 }_250 : undefined,_250 });_250_250 return API.success(_250 {_250 success: true,_250 key: multipartUpload.key,_250 uploadId: multipartUpload.uploadId,_250 },_250 request_250 );_250 } catch (error) {_250 console.error("Failed to create multipart upload:", error);_250 return API.error("Failed to create multipart upload", request, 500);_250 }_250 }_250_250 case "complete": {_250 // Complete a multipart upload_250 const body: CompleteMultipartRequest = await request.json();_250_250 if (!body.uploadId || !body.key || !body.parts) {_250 return API.error("Missing required parameters", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(_250 body.key,_250 body.uploadId_250 );_250_250 // Parts are already in R2UploadedPart format_250 const r2Parts = body.parts;_250_250 const object = await multipartUpload.complete(r2Parts);_250_250 return API.success(_250 {_250 success: true,_250 key: object.key,_250 etag: object.httpEtag,_250 size: object.size,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to complete multipart upload:", error);_250 return API.error(_250 error.message || "Failed to complete multipart upload",_250 request,_250 400_250 );_250 }_250 }_250_250 default:_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250 } catch (error) {_250 console.error("Multipart upload error:", error);_250 return API.error("Multipart upload failed", request, 500);_250 }_250};_250_250// Uploads individual parts of a multipart upload_250export const PUT: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "upload-part") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const partNumberStr = url.searchParams.get("partNumber");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !partNumberStr || !key) {_250 return API.error("Missing uploadId, partNumber, or key", request, 400);_250 }_250_250 const partNumber = parseInt(partNumberStr);_250 if (isNaN(partNumber) || partNumber < 1) {_250 return API.error("Invalid part number", request, 400);_250 }_250_250 if (!request.body) {_250 return API.error("Missing request body", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250_250 // Convert request body to ArrayBuffer to get known length_250 const arrayBuffer = await request.arrayBuffer();_250 const uploadedPart = await multipartUpload.uploadPart(_250 partNumber,_250 arrayBuffer_250 );_250_250 return API.success(_250 {_250 success: true,_250 partNumber: uploadedPart.partNumber,_250 etag: uploadedPart.etag,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to upload part:", error);_250 return API.error(error.message || "Failed to upload part", request, 400);_250 }_250 } catch (error) {_250 console.error("Upload part error:", error);_250 return API.error("Upload part failed", request, 500);_250 }_250};_250_250// Aborts a multipart upload_250export const DELETE: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "abort") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !key) {_250 return API.error("Missing uploadId or key", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250 await multipartUpload.abort();_250_250 return API.success(_250 {_250 success: true,_250 message: "Multipart upload aborted successfully",_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to abort multipart upload:", error);_250 return API.error(_250 error.message || "Failed to abort multipart upload",_250 request,_250 400_250 );_250 }_250 } catch (error) {_250 console.error("Abort multipart upload error:", error);_250 return API.error("Abort multipart upload failed", request, 500);_250 }_250};_250_250export const OPTIONS: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250 return API.cors(request);_250};
Once all parts are uploaded through the PUT endpoint, the browser will send a request to this POST endpoint with the action parameter set to complete with an ordered list of all uploaded parts with their corresponding entity tags (ETags).
When action=complete, this endpoint consolidates all uploaded parts into a single file in Object Storage using multipartUpload.complete() method
Create the POST Endpoint
The POST endpoint manages multipart uploads through two key operations: initializing new uploads and finalizing completed ones. The action query parameter determines which operation to perform.
Be sure to include the locals parameter in the POST endpoint to access your environment variables and CLOUD_FILES binding.
Setup CORS handling
Since multipart uploads require direct communication with the worker domain (not your Webflow Cloud domain), you need to set up proper CORS configuration. The imported API utility automatically handles CORS headers and preflight requests to ensure seamless cross-origin communication.
Access your Object Storage bucket
Retrieve the CLOUD_FILES binding that provides access to your Object Storage bucket. This binding contains all the multipart upload methods you'll need throughout the upload process.
Get Action Parameter
Parse the action query parameter to route between different multipart upload operations. The POST endpoint supports both create and complete actions.
Initialize multipart upload session
When action=create, this code establishes a new multipart upload session in Object Storage. The browser provides the key and contentType, then receives a unique uploadId that identifies the entire upload process.
Finalize multipart upload
Once all parts are uploaded through the PUT endpoint, the browser will send a request to this POST endpoint with the action parameter set to complete with an ordered list of all uploaded parts with their corresponding entity tags (ETags).
When action=complete, this endpoint consolidates all uploaded parts into a single file in Object Storage using multipartUpload.complete() method
_250import type { APIRoute } from "astro";_250import { API } from "../../utils/api";_250_250interface MultipartUploadRequest {_250 key: string;_250 contentType?: string;_250}_250_250interface CompleteMultipartRequest {_250 uploadId: string;_250 key: string;_250 parts: R2UploadedPart[];_250}_250_250// Creates and completes a new multipart upload session_250export const POST: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (!action) {_250 return API.error("Missing action parameter", request, 400);_250 }_250_250 switch (action) {_250 case "create": {_250 // Create a new multipart upload_250 const body: MultipartUploadRequest = await request.json();_250_250 if (!body.key) {_250 return API.error("Missing key parameter", request, 400);_250 }_250_250 try {_250 const multipartUpload = await bucket.createMultipartUpload(body.key, {_250 httpMetadata: body.contentType_250 ? {_250 contentType: body.contentType,_250 }_250 : undefined,_250 });_250_250 return API.success(_250 {_250 success: true,_250 key: multipartUpload.key,_250 uploadId: multipartUpload.uploadId,_250 },_250 request_250 );_250 } catch (error) {_250 console.error("Failed to create multipart upload:", error);_250 return API.error("Failed to create multipart upload", request, 500);_250 }_250 }_250_250 case "complete": {_250 // Complete a multipart upload_250 const body: CompleteMultipartRequest = await request.json();_250_250 if (!body.uploadId || !body.key || !body.parts) {_250 return API.error("Missing required parameters", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(_250 body.key,_250 body.uploadId_250 );_250_250 // Parts are already in R2UploadedPart format_250 const r2Parts = body.parts;_250_250 const object = await multipartUpload.complete(r2Parts);_250_250 return API.success(_250 {_250 success: true,_250 key: object.key,_250 etag: object.httpEtag,_250 size: object.size,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to complete multipart upload:", error);_250 return API.error(_250 error.message || "Failed to complete multipart upload",_250 request,_250 400_250 );_250 }_250 }_250_250 default:_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250 } catch (error) {_250 console.error("Multipart upload error:", error);_250 return API.error("Multipart upload failed", request, 500);_250 }_250};_250_250// Uploads individual parts of a multipart upload_250export const PUT: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "upload-part") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const partNumberStr = url.searchParams.get("partNumber");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !partNumberStr || !key) {_250 return API.error("Missing uploadId, partNumber, or key", request, 400);_250 }_250_250 const partNumber = parseInt(partNumberStr);_250 if (isNaN(partNumber) || partNumber < 1) {_250 return API.error("Invalid part number", request, 400);_250 }_250_250 if (!request.body) {_250 return API.error("Missing request body", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250_250 // Convert request body to ArrayBuffer to get known length_250 const arrayBuffer = await request.arrayBuffer();_250 const uploadedPart = await multipartUpload.uploadPart(_250 partNumber,_250 arrayBuffer_250 );_250_250 return API.success(_250 {_250 success: true,_250 partNumber: uploadedPart.partNumber,_250 etag: uploadedPart.etag,_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to upload part:", error);_250 return API.error(error.message || "Failed to upload part", request, 400);_250 }_250 } catch (error) {_250 console.error("Upload part error:", error);_250 return API.error("Upload part failed", request, 500);_250 }_250};_250_250// Aborts a multipart upload_250export const DELETE: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250_250 // Handle CORS preflight requests_250 if (request.method === "OPTIONS") {_250 console.log("CORS preflight request from:", request.headers.get("Origin"));_250 return API.cors(request);_250 }_250_250 try {_250 // Check if bucket is available_250 const bucket = locals.runtime.env.CLOUD_FILES;_250 if (!bucket) {_250 return API.error("Cloud storage not configured", request, 500);_250 }_250_250 const url = new URL(request.url);_250 const action = url.searchParams.get("action");_250_250 if (action !== "abort") {_250 return API.error(`Unknown action: ${action}`, request, 400);_250 }_250_250 const uploadId = url.searchParams.get("uploadId");_250 const key = url.searchParams.get("key");_250_250 if (!uploadId || !key) {_250 return API.error("Missing uploadId or key", request, 400);_250 }_250_250 try {_250 const multipartUpload = bucket.resumeMultipartUpload(key, uploadId);_250 await multipartUpload.abort();_250_250 return API.success(_250 {_250 success: true,_250 message: "Multipart upload aborted successfully",_250 },_250 request_250 );_250 } catch (error: any) {_250 console.error("Failed to abort multipart upload:", error);_250 return API.error(_250 error.message || "Failed to abort multipart upload",_250 request,_250 400_250 );_250 }_250 } catch (error) {_250 console.error("Abort multipart upload error:", error);_250 return API.error("Abort multipart upload failed", request, 500);_250 }_250};_250_250export const OPTIONS: APIRoute = async ({ request, locals }) => {_250 // Set the origin for the API_250 API.init(locals.runtime.env.ORIGIN);_250 return API.cors(request);_250};