535 lines
16 KiB
PHP
535 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Requests\FileUploadRequest;
|
|
use App\Models\File;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
|
|
class FileController extends Controller
|
|
{
|
|
/**
|
|
* 文件列表
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = File::with('user')->orderBy('created_at', 'desc');
|
|
|
|
// 筛选条件
|
|
if ($request->filled('user_id')) {
|
|
$query->where('user_id', $request->user_id);
|
|
}
|
|
|
|
if ($request->filled('module')) {
|
|
$query->where('module', $request->module);
|
|
}
|
|
|
|
if ($request->filled('purpose')) {
|
|
$query->where('purpose', $request->purpose);
|
|
}
|
|
|
|
if ($request->filled('mime_type')) {
|
|
$query->where('mime_type', 'like', "%{$request->mime_type}%");
|
|
}
|
|
|
|
if ($request->filled('original_name')) {
|
|
$query->where('original_name', 'like', "%{$request->original_name}%");
|
|
}
|
|
|
|
if ($request->filled('status')) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
|
|
if ($request->filled('date_range')) {
|
|
$dates = explode(',', $request->date_range);
|
|
if (count($dates) === 2) {
|
|
$query->whereBetween('created_at', [$dates[0], $dates[1]]);
|
|
}
|
|
}
|
|
|
|
if ($request->filled('keyword')) {
|
|
$keyword = $request->keyword;
|
|
$query->where(function ($q) use ($keyword) {
|
|
$q->where('original_name', 'like', "%{$keyword}%")
|
|
->orWhere('description', 'like', "%{$keyword}%")
|
|
->orWhere('module', 'like', "%{$keyword}%")
|
|
->orWhere('purpose', 'like', "%{$keyword}%");
|
|
});
|
|
}
|
|
|
|
$perPage = $request->input('limit', 20);
|
|
$files = $query->paginate($perPage);
|
|
|
|
// 添加额外信息
|
|
$files->getCollection()->transform(function ($file) {
|
|
$file->url = $file->getUrl();
|
|
$file->size_formatted = $file->size_formatted;
|
|
$file->icon = $file->icon;
|
|
$file->exists = $file->exists();
|
|
return $file;
|
|
});
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => [
|
|
'list' => $files->items(),
|
|
'total' => $files->total(),
|
|
'current_page' => $files->currentPage(),
|
|
'last_page' => $files->lastPage(),
|
|
],
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 文件详情
|
|
*/
|
|
public function show(string $id)
|
|
{
|
|
$file = File::with('user')->find($id);
|
|
|
|
if (!$file) {
|
|
return response()->json([
|
|
'code' => 404,
|
|
'message' => '文件不存在'
|
|
], 404);
|
|
}
|
|
|
|
$file->url = $file->getUrl();
|
|
$file->size_formatted = $file->size_formatted;
|
|
$file->icon = $file->icon;
|
|
$file->exists = $file->exists();
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $file,
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 上传文件
|
|
*/
|
|
public function upload(FileUploadRequest $request)
|
|
{
|
|
try {
|
|
$file = $request->file('file');
|
|
$module = $request->input('module', 'default');
|
|
$purpose = $request->input('purpose', 'upload');
|
|
$description = $request->input('description');
|
|
|
|
// 生成存储文件名
|
|
$originalName = $file->getClientOriginalName();
|
|
$extension = $file->getClientOriginalExtension();
|
|
$mimeType = $file->getMimeType();
|
|
$size = $file->getSize();
|
|
|
|
$storageName = Str::uuid() . '.' . $extension;
|
|
$path = 'uploads/' . date('Y/m/d');
|
|
|
|
// 存储文件
|
|
$disk = config('filesystems.default', 'public');
|
|
$filePath = $file->storeAs($path, $storageName, $disk);
|
|
|
|
// 创建文件记录
|
|
$fileRecord = File::create([
|
|
'user_id' => auth()->id(),
|
|
'original_name' => $originalName,
|
|
'storage_name' => $storageName,
|
|
'path' => $path,
|
|
'url' => Storage::disk($disk)->url($filePath),
|
|
'mime_type' => $mimeType,
|
|
'size' => $size,
|
|
'extension' => $extension,
|
|
'disk' => $disk,
|
|
'module' => $module,
|
|
'purpose' => $purpose,
|
|
'description' => $description,
|
|
'status' => 'active',
|
|
]);
|
|
|
|
$fileRecord->url = $fileRecord->getUrl();
|
|
$fileRecord->size_formatted = $fileRecord->size_formatted;
|
|
$fileRecord->icon = $fileRecord->icon;
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $fileRecord,
|
|
'message' => '文件上传成功'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '文件上传失败: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 批量上传文件
|
|
*/
|
|
public function batchUpload(Request $request)
|
|
{
|
|
$request->validate([
|
|
'files' => 'required|array|min:1|max:10',
|
|
'files.*' => 'file|max:10240', // 10MB per file
|
|
'module' => 'nullable|string|max:50',
|
|
'purpose' => 'nullable|string|max:50',
|
|
]);
|
|
|
|
$uploadedFiles = [];
|
|
$failedFiles = [];
|
|
|
|
foreach ($request->file('files') as $file) {
|
|
try {
|
|
$originalName = $file->getClientOriginalName();
|
|
$extension = $file->getClientOriginalExtension();
|
|
$storageName = Str::uuid() . '.' . $extension;
|
|
$path = 'uploads/' . date('Y/m/d');
|
|
|
|
$disk = config('filesystems.default', 'public');
|
|
$filePath = $file->storeAs($path, $storageName, $disk);
|
|
|
|
$fileRecord = File::create([
|
|
'user_id' => auth()->id(),
|
|
'original_name' => $originalName,
|
|
'storage_name' => $storageName,
|
|
'path' => $path,
|
|
'url' => Storage::disk($disk)->url($filePath),
|
|
'mime_type' => $file->getMimeType(),
|
|
'size' => $file->getSize(),
|
|
'extension' => $extension,
|
|
'disk' => $disk,
|
|
'module' => $request->input('module', 'default'),
|
|
'purpose' => $request->input('purpose', 'upload'),
|
|
'status' => 'active',
|
|
]);
|
|
|
|
$uploadedFiles[] = $fileRecord;
|
|
} catch (\Exception $e) {
|
|
$failedFiles[] = [
|
|
'name' => $originalName ?? '未知文件',
|
|
'error' => $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => [
|
|
'uploaded_count' => count($uploadedFiles),
|
|
'failed_count' => count($failedFiles),
|
|
'uploaded_files' => $uploadedFiles,
|
|
'failed_files' => $failedFiles,
|
|
],
|
|
'message' => "批量上传完成,成功 {$uploadedCount} 个,失败 {$failedCount} 个"
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 更新文件信息
|
|
*/
|
|
public function update(Request $request, string $id)
|
|
{
|
|
$file = File::find($id);
|
|
|
|
if (!$file) {
|
|
return response()->json([
|
|
'code' => 404,
|
|
'message' => '文件不存在'
|
|
], 404);
|
|
}
|
|
|
|
$request->validate([
|
|
'original_name' => 'nullable|string|max:255',
|
|
'description' => 'nullable|string|max:500',
|
|
'module' => 'nullable|string|max:50',
|
|
'purpose' => 'nullable|string|max:50',
|
|
'status' => 'nullable|string|in:active,inactive,deleted',
|
|
]);
|
|
|
|
try {
|
|
$file->update($request->only([
|
|
'original_name', 'description', 'module', 'purpose', 'status'
|
|
]));
|
|
|
|
$file->url = $file->getUrl();
|
|
$file->size_formatted = $file->size_formatted;
|
|
$file->icon = $file->icon;
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $file,
|
|
'message' => '文件信息更新成功'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '更新失败: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除文件
|
|
*/
|
|
public function destroy(string $id)
|
|
{
|
|
$file = File::find($id);
|
|
|
|
if (!$file) {
|
|
return response()->json([
|
|
'code' => 404,
|
|
'message' => '文件不存在'
|
|
], 404);
|
|
}
|
|
|
|
try {
|
|
// 删除物理文件
|
|
$file->deletePhysicalFile();
|
|
|
|
// 删除数据库记录
|
|
$file->delete();
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'message' => '文件删除成功'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '删除失败: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 批量删除文件
|
|
*/
|
|
public function batchDelete(Request $request)
|
|
{
|
|
$request->validate([
|
|
'file_ids' => 'required|array|min:1',
|
|
'file_ids.*' => 'integer|exists:files,id',
|
|
]);
|
|
|
|
$successCount = 0;
|
|
$failedFiles = [];
|
|
|
|
try {
|
|
foreach ($request->file_ids as $fileId) {
|
|
$file = File::find($fileId);
|
|
|
|
if (!$file) {
|
|
$failedFiles[] = ['id' => $fileId, 'reason' => '文件不存在'];
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
$file->deletePhysicalFile();
|
|
$file->delete();
|
|
$successCount++;
|
|
} catch (\Exception $e) {
|
|
$failedFiles[] = [
|
|
'id' => $fileId,
|
|
'name' => $file->original_name,
|
|
'reason' => $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => [
|
|
'success_count' => $successCount,
|
|
'failed_files' => $failedFiles,
|
|
],
|
|
'message' => "批量删除成功,成功 {$successCount} 个"
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '批量删除失败: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 下载文件
|
|
*/
|
|
public function download(string $id)
|
|
{
|
|
$file = File::find($id);
|
|
|
|
if (!$file) {
|
|
return response()->json([
|
|
'code' => 404,
|
|
'message' => '文件不存在'
|
|
], 404);
|
|
}
|
|
|
|
if (!$file->exists()) {
|
|
return response()->json([
|
|
'code' => 404,
|
|
'message' => '物理文件不存在'
|
|
], 404);
|
|
}
|
|
|
|
try {
|
|
$path = $file->getFullPathAttribute();
|
|
$disk = $file->disk;
|
|
|
|
return Storage::disk($disk)->download($path, $file->original_name);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '文件下载失败: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 预览文件
|
|
*/
|
|
public function preview(string $id)
|
|
{
|
|
$file = File::find($id);
|
|
|
|
if (!$file) {
|
|
return response()->json([
|
|
'code' => 404,
|
|
'message' => '文件不存在'
|
|
], 404);
|
|
}
|
|
|
|
if (!$file->exists()) {
|
|
return response()->json([
|
|
'code' => 404,
|
|
'message' => '物理文件不存在'
|
|
], 404);
|
|
}
|
|
|
|
// 检查文件类型是否支持预览
|
|
$previewableTypes = [
|
|
'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/svg+xml', 'image/webp',
|
|
'application/pdf',
|
|
'text/plain', 'text/html', 'text/css', 'text/javascript',
|
|
'application/json', 'application/xml',
|
|
];
|
|
|
|
if (!in_array($file->mime_type, $previewableTypes)) {
|
|
return response()->json([
|
|
'code' => 400,
|
|
'message' => '该文件类型不支持预览'
|
|
], 400);
|
|
}
|
|
|
|
try {
|
|
$path = $file->getFullPathAttribute();
|
|
$disk = $file->disk;
|
|
|
|
$content = Storage::disk($disk)->get($path);
|
|
|
|
return response($content)
|
|
->header('Content-Type', $file->mime_type)
|
|
->header('Content-Disposition', 'inline; filename="' . $file->original_name . '"');
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'code' => 500,
|
|
'message' => '文件预览失败: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取文件统计
|
|
*/
|
|
public function statistics(Request $request)
|
|
{
|
|
$userId = $request->input('user_id');
|
|
|
|
$stats = File::getStatistics($userId);
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $stats,
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 获取模块列表
|
|
*/
|
|
public function getModules()
|
|
{
|
|
$modules = File::select('module')
|
|
->distinct()
|
|
->orderBy('module', 'asc')
|
|
->pluck('module');
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $modules,
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 清理过期文件
|
|
*/
|
|
public function cleanup(Request $request)
|
|
{
|
|
$request->validate([
|
|
'days' => 'nullable|integer|min:1|max:365',
|
|
]);
|
|
|
|
$days = $request->input('days', 30);
|
|
|
|
$result = File::cleanupExpired($days);
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $result,
|
|
'message' => "清理完成,删除 {$result['deleted_count']} 个文件"
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 获取上传配置
|
|
*/
|
|
public function getUploadConfig()
|
|
{
|
|
$config = [
|
|
'max_size' => 10240, // 10MB in KB
|
|
'allowed_types' => [
|
|
'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/svg+xml', 'image/webp',
|
|
'application/pdf',
|
|
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
'text/plain', 'text/csv',
|
|
'application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed',
|
|
'application/json', 'application/xml',
|
|
],
|
|
'allowed_extensions' => [
|
|
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp',
|
|
'pdf',
|
|
'doc', 'docx',
|
|
'xls', 'xlsx',
|
|
'ppt', 'pptx',
|
|
'txt', 'csv',
|
|
'zip', 'rar', '7z',
|
|
'json', 'xml',
|
|
],
|
|
'max_files_per_request' => 10,
|
|
];
|
|
|
|
return response()->json([
|
|
'code' => 200,
|
|
'data' => $config,
|
|
'message' => 'success'
|
|
]);
|
|
}
|
|
} |