From 6a94d51032a0e75722f982c1fe5b09c5d529c86d Mon Sep 17 00:00:00 2001 From: Bivekich Date: Thu, 17 Jul 2025 13:12:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20API=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BA=D0=B0=D1=87?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=84=D0=B0=D0=B9=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2:=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA,=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=D0=B6=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=B4=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?Unicode,=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B2=D0=B5=D1=82=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=BA?= =?UTF-8?q?=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=D0=B3=D0=BE=20?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F.?= =?UTF-8?q?=20=D0=9B=D0=BE=D0=B3=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA=20=D0=B8=20=D1=83?= =?UTF-8?q?=D1=81=D0=BF=D0=B5=D1=88=D0=BD=D1=8B=D1=85=20=D0=B7=D0=B0=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B7=D0=BE=D0=BA=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=20=D0=B8=D0=BD=D1=84?= =?UTF-8?q?=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8=D0=B2=D0=BD=D0=BE.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/download-file/route.ts | 73 ++++++++++++++++-------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/app/api/download-file/route.ts b/src/app/api/download-file/route.ts index bb1fb78..b3fab84 100644 --- a/src/app/api/download-file/route.ts +++ b/src/app/api/download-file/route.ts @@ -1,64 +1,71 @@ -import { NextRequest, NextResponse } from 'next/server' +import { NextRequest, NextResponse } from 'next/server'; export async function GET(request: NextRequest) { try { - const searchParams = request.nextUrl.searchParams - const fileUrl = searchParams.get('url') - const fileName = searchParams.get('filename') + const searchParams = request.nextUrl.searchParams; + const fileUrl = searchParams.get('url'); + const fileName = searchParams.get('filename'); + + console.log('🔽 Проксируем скачивание файла:', fileUrl); if (!fileUrl) { return NextResponse.json( - { error: 'File URL is required' }, + { error: 'URL файла не предоставлен' }, { status: 400 } - ) + ); } - // Проверяем, что URL принадлежит нашему S3 хранилищу - if (!fileUrl.includes('s3.twcstorage.ru/617774af-sfera/')) { + // Проверяем, что URL начинается с нашего доверенного домена + if (!fileUrl.startsWith('https://s3.twcstorage.ru/')) { return NextResponse.json( - { error: 'Invalid file URL' }, + { error: 'Недопустимый URL файла' }, { status: 400 } - ) + ); } - console.log('🔽 Проксируем скачивание файла:', fileUrl) - // Загружаем файл с S3 - const response = await fetch(fileUrl, { - method: 'GET', - headers: { - 'User-Agent': 'Mozilla/5.0 (compatible; SferaApp/1.0)', - } - }) - + const response = await fetch(fileUrl); + if (!response.ok) { - console.error('❌ Ошибка загрузки с S3:', response.status, response.statusText) + console.error('❌ Ошибка загрузки файла с S3:', response.status, response.statusText); return NextResponse.json( - { error: `Failed to fetch file: ${response.status}` }, - { status: response.status } - ) + { error: 'Файл не найден' }, + { status: 404 } + ); } - // Получаем содержимое файла - const buffer = await response.arrayBuffer() + // Получаем буфер файла + const arrayBuffer = await response.arrayBuffer(); + const buffer = new Uint8Array(arrayBuffer); - console.log('✅ Файл успешно загружен с S3, размер:', buffer.byteLength) + console.log('✅ Файл успешно загружен с S3, размер:', buffer.length); + + // Определяем MIME-тип из исходного ответа или устанавливаем по умолчанию + const contentType = response.headers.get('content-type') || 'application/octet-stream'; + + // Правильно кодируем имя файла для поддержки Unicode символов + let contentDisposition = 'attachment'; + if (fileName) { + // Используем RFC 5987 кодирование для поддержки Unicode + const encodedFileName = encodeURIComponent(fileName); + contentDisposition = `attachment; filename*=UTF-8''${encodedFileName}`; + } // Возвращаем файл с правильными заголовками для скачивания return new NextResponse(buffer, { headers: { - 'Content-Type': 'application/octet-stream', // Принудительное скачивание - 'Content-Disposition': `attachment; filename="${fileName || 'file'}"`, - 'Content-Length': buffer.byteLength.toString(), + 'Content-Type': contentType, + 'Content-Disposition': contentDisposition, + 'Content-Length': buffer.length.toString(), 'Cache-Control': 'no-cache', }, - }) + }); } catch (error) { - console.error('❌ Ошибка в download-file API:', error) + console.error('❌ Ошибка в download-file API:', error); return NextResponse.json( - { error: 'Failed to download file' }, + { error: 'Внутренняя ошибка сервера' }, { status: 500 } - ) + ); } } \ No newline at end of file