import { NextRequest, NextResponse } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { prisma } from '@/lib/prisma' import { readFile, access } from 'fs/promises' import path from 'path' export async function GET(req: NextRequest) { try { // Check if user is logged in const session = await getServerSession(authOptions) if (!session) { return NextResponse.json( { error: '请先登录' }, { status: 401 } ) } // Check if user has download permission (skip check for admins) const userId = (session.user as any).id const userRole = (session.user as any).role if (userRole !== 'ADMIN') { const user = await prisma.user.findUnique({ where: { id: userId }, select: { canDownload: true } }) if (!user || !user.canDownload) { return NextResponse.json( { error: '您的下载权限已被限制,请联系管理员' }, { status: 403 } ) } } const { searchParams } = new URL(req.url) const id = searchParams.get('id') if (!id) { return NextResponse.json( { error: '缺少素材ID' }, { status: 400 } ) } const material = await prisma.material.findUnique({ where: { id }, }) if (!material) { return NextResponse.json( { error: '素材不存在' }, { status: 404 } ) } // 增加下载次数 await prisma.material.update({ where: { id }, data: { downloadCount: { increment: 1 } } }) // Read file from local storage // 确保使用绝对路径 let filePath = material.zipFilePath if (!path.isAbsolute(filePath)) { filePath = path.join(process.cwd(), filePath) } console.log('Attempting to download file:', filePath) // 检查文件是否存在 try { await access(filePath) } catch (error) { console.error('File not found:', filePath) return NextResponse.json( { error: '文件不存在,可能已被删除' }, { status: 404 } ) } const fileBuffer = await readFile(filePath) // 生成新的文件名:SPU_一级分类_二级分类_用户名.扩展名 // 获取原文件扩展名 const originalExt = path.extname(material.zipFileName) // 例如 .zip 或 .7z 或视频扩展名 const newFileName = `${material.productModel}_${material.categoryName}_${material.sceneName}_${material.authorName}${originalExt}` console.log('Download filename:', newFileName) // 根据文件类型和素材类型确定Content-Type let contentType = 'application/zip' let contentDisposition = `attachment; filename="${encodeURIComponent(newFileName)}"` // 如果是视频类型,使用视频的Content-Type并支持流式传输 if (material.materialType === '视频') { const ext = originalExt.toLowerCase() const videoTypes: { [key: string]: string } = { '.mp4': 'video/mp4', '.avi': 'video/x-msvideo', '.mov': 'video/quicktime', '.wmv': 'video/x-ms-wmv', '.flv': 'video/x-flv', '.mkv': 'video/x-matroska', '.webm': 'video/webm', } contentType = videoTypes[ext] || 'video/mp4' // 视频文件支持流式传输(Range请求) const range = req.headers.get('range') if (range) { // 支持视频流式传输 const fileSize = fileBuffer.length const parts = range.replace(/bytes=/, '').split('-') const start = parseInt(parts[0], 10) const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1 const chunkSize = end - start + 1 const chunk = fileBuffer.slice(start, end + 1) return new NextResponse(chunk as any, { status: 206, headers: { 'Content-Type': contentType, 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunkSize.toString(), }, }) } // 非Range请求,返回完整文件(但允许内联播放) contentDisposition = `inline; filename="${encodeURIComponent(newFileName)}"` } return new NextResponse(fileBuffer as any, { headers: { 'Content-Type': contentType, 'Content-Disposition': contentDisposition, 'Content-Length': fileBuffer.length.toString(), 'Accept-Ranges': 'bytes', // 支持Range请求(用于视频流式传输) }, }) } catch (error) { console.error('Download error:', error) return NextResponse.json( { error: '下载失败: ' + (error instanceof Error ? error.message : '未知错误') }, { status: 500 } ) } }