本文目录导读:

在 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>
安全建议
- 设置合理的过期时间:根据文件敏感度设置不同的有效期
- 限制下载次数:对于重要文件可以限制下载次数
- 记录下载日志:记录谁在何时下载了什么文件
- 文件权限控制:确保只有授权用户才能生成链接
选择哪种方案取决于你的具体需求:
- 签名路由:最安全,适合简单场景
- 缓存机制:可以控制下载次数,适合一次性下载
- S3临时URL:适合云存储,性能最好