PHP项目Laravel临时文件下载链接

wen PHP项目 8

本文目录导读:

PHP项目Laravel临时文件下载链接

  1. 使用临时签名路由(推荐)
  2. 使用控制器方法
  3. 使用缓存存储临时代理
  4. 使用 Laravel 的临时URL方法
  5. 综合示例:完整的下载链接控制器
  6. 前端调用示例
  7. 安全建议

在 Laravel 中实现临时文件下载链接有多种方式,以下是几种常用的解决方案:

使用临时签名路由(推荐)

生成临时下载链接

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
// 在路由中定义下载路由
Route::get('/download/{file}', function ($file) {
    // 验证文件存在
    if (!Storage::disk('local')->exists($file)) {
        abort(404);
    }
    // 获取文件信息
    $filePath = Storage::disk('local')->path($file);
    $fileName = basename($file);
    // 返回下载响应
    return response()->download($filePath, $fileName);
})->middleware('signed'); // 使用签名中间件
// 生成临时链接
$url = URL::temporarySignedRoute(
    'download', // 路由名称
    now()->addMinutes(30), // 30分钟后过期
    ['file' => '12345/report.pdf'] // 参数
);

使用控制器方法

// 控制器
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class FileDownloadController extends Controller
{
    public function download(Request $request, $file)
    {
        // 验证签名
        if (!$request->hasValidSignature()) {
            abort(401);
        }
        // 验证文件存在
        if (!Storage::disk('local')->exists($file)) {
            abort(404);
        }
        // 文件下载
        return Storage::disk('local')->download($file);
    }
    // 生成下载链接
    public function generateLink(Request $request)
    {
        $file = '12345/report.pdf';
        $expires = now()->addHours(2);
        $url = URL::temporarySignedRoute(
            'file.download',
            $expires,
            ['file' => $file]
        );
        return response()->json([
            'download_url' => $url,
            'expires_at' => $expires->toDateTimeString()
        ]);
    }
}
// 路由
Route::get('/files/download/{file}', [FileDownloadController::class, 'download'])
    ->name('file.download')
    ->middleware('signed');

使用缓存存储临时代理

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class FileProxyController extends Controller
{
    public function createTemporaryLink(Request $request)
    {
        $file = $request->input('file');
        $minutes = $request->input('expires', 30);
        // 生成唯一token
        $token = Str::random(40);
        // 存储到缓存
        Cache::put(
            "file_token_{$token}", 
            ['file' => $file],
            now()->addMinutes($minutes)
        );
        return url("/temp-download/{$token}");
    }
    public function downloadFile($token)
    {
        // 从缓存获取文件信息
        $data = Cache::get("file_token_{$token}");
        if (!$data) {
            abort(404, '链接已过期');
        }
        $file = $data['file'];
        // 清理token(一次性使用)
        Cache::forget("file_token_{$token}");
        return response()->download(storage_path("app/{$file}"));
    }
}
// 路由
Route::post('/create-temp-link', [FileProxyController::class, 'createTemporaryLink']);
Route::get('/temp-download/{token}', [FileProxyController::class, 'downloadFile']);

使用 Laravel 的临时URL方法

use Illuminate\Support\Facades\Storage;
// 获取 S3 临时URL
$url = Storage::disk('s3')->temporaryUrl(
    'path/to/file.pdf', // 文件路径
    now()->addMinutes(10), // 有效期
    [
        'ResponseContentType' => 'application/pdf',
        'ResponseContentDisposition' => 'attachment; filename="file.pdf"',
    ]
);

综合示例:完整的下载链接控制器

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
class TemporaryDownloadController extends Controller
{
    /**
     * 生成临时下载链接
     */
    public function generate(Request $request)
    {
        $request->validate([
            'file' => 'required|string',
            'expires' => 'nullable|integer|min:1|max:1440', // 分钟数
        ]);
        $file = $request->file;
        $expires = $request->expires ?? 30;
        // 检查文件是否存在
        if (!Storage::exists($file)) {
            return response()->json(['error' => '文件不存在'], 404);
        }
        // 生成签名URL
        $url = URL::temporarySignedRoute(
            'download.temp',
            now()->addMinutes($expires),
            ['file' => $file]
        );
        return response()->json([
            'success' => true,
            'url' => $url,
            'expires_in' => $expires . ' minutes',
            'expires_at' => now()->addMinutes($expires)->toDateTimeString()
        ]);
    }
    /**
     * 处理下载请求
     */
    public function download(Request $request)
    {
        // 验证签名
        if (!$request->hasValidSignature()) {
            abort(403, '下载链接无效或已过期');
        }
        $file = $request->file;
        // 检查文件存在
        if (!Storage::exists($file)) {
            abort(404, '文件不存在');
        }
        // 生成文件名
        $filename = basename($file);
        // 返回下载
        return Storage::download($file, $filename);
    }
}
// 路由定义
Route::post('/generate-download-link', [TemporaryDownloadController::class, 'generate']);
Route::get('/download/temp/{file}', [TemporaryDownloadController::class, 'download'])
    ->name('download.temp')
    ->middleware('signed');

前端调用示例

<!-- 生成链接表单 -->
<form id="generateForm">
    <input type="text" name="file" placeholder="文件路径" required>
    <input type="number" name="expires" placeholder="有效期(分钟)" value="30" min="1" max="1440">
    <button type="submit">生成下载链接</button>
</form>
<!-- JavaScript -->
<script>
document.getElementById('generateForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    try {
        const response = await fetch('/generate-download-link', {
            method: 'POST',
            headers: {
                'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                file: formData.get('file'),
                expires: parseInt(formData.get('expires'))
            })
        });
        const data = await response.json();
        if (data.success) {
            console.log('下载链接:', data.url);
            console.log('过期时间:', data.expires_at);
            // 显示链接
            document.getElementById('downloadLink').innerHTML = 
                `<a href="${data.url}" target="_blank">下载文件</a>`;
        }
    } catch (error) {
        console.error('生成链接失败:', error);
    }
});
</script>

安全建议

  1. 设置合理的过期时间:根据文件敏感度设置不同的有效期
  2. 限制下载次数:对于重要文件可以限制下载次数
  3. 记录下载日志:记录谁在何时下载了什么文件
  4. 文件权限控制:确保只有授权用户才能生成链接

选择哪种方案取决于你的具体需求:

  • 签名路由:最安全,适合简单场景
  • 缓存机制:可以控制下载次数,适合一次性下载
  • S3临时URL:适合云存储,性能最好

抱歉,评论功能暂时关闭!