Laravel Inertia/Vue3提交表单下载文件返回302重定向问题
问题原因
Inertia的useForm.post()本质是发送AJAX请求,但浏览器不会自动触发文件下载动作来处理AJAX响应中的文件流。Laravel的response()->download()在这种场景下,会被Inertia的默认逻辑识别为页面跳转请求,最终导致302重定向并刷新页面,无法触发下载。
解决方案
方案一:后端生成临时签名URL,前端跳转
后端生成动态文件后,创建一个带签名的临时下载链接,通过Inertia的location()方法触发前端跳转,让浏览器处理下载:
- 修改控制器代码:
use Illuminate\Http\Request; use Illuminate\Support\Facades\URL; public function __invoke(Request $request) { // 保留你的动态生成文件逻辑 $path = '/some/path/to/report.xls'; $filename = 'report.xls'; // 生成有效期5分钟的签名URL,防止非法访问 $downloadUrl = URL::temporarySignedRoute( 'temp.download', now()->addMinutes(5), ['path' => $path, 'filename' => $filename] ); // 通知Inertia跳转至下载链接 return inertia()->location($downloadUrl); }
- 添加临时下载路由:
Route::get('temp-download/{path}/{filename}', function ($path, $filename) { if (!URL::hasValidSignature(request())) { abort(403); } return response()->download($path, $filename); })->name('temp.download');
方案二:前端处理Blob响应,手动触发下载
直接通过AJAX接收文件Blob,前端创建临时下载链接触发下载:
- 后端控制器无需大改,确保返回正确的文件响应:
public function __invoke(Request $request) { $path = '/some/path/to/report.xls'; return response()->download($path, 'report.xls'); }
- 修改前端代码(替换useForm逻辑):
<script setup> import axios from 'axios'; import { useRoute } from 'vue-router'; import { ref } from 'vue'; const route = useRoute(); const some = ref(null); const fields = ref(null); const handleSubmit = async () => { try { const res = await axios.post(route('report'), { some: some.value, fields: fields.value, }, { responseType: 'blob' // 必须指定响应类型为Blob }); // 创建临时下载链接并触发下载 const url = window.URL.createObjectURL(new Blob([res.data])); const a = document.createElement('a'); a.href = url; a.download = 'report.xls'; document.body.appendChild(a); a.click(); // 清理资源 a.remove(); window.URL.revokeObjectURL(url); } catch (err) { console.error('下载失败:', err); } }; </script> <form @submit.prevent="handleSubmit"> <input v-model="some" /> <input v-model="fields" /> <button type="submit">download</button> </form>
方案三:使用原生HTML表单提交(最简单)
直接复用Blade中的表单逻辑,绕过Inertia的AJAX处理:
<script setup> import { page } from '@inertiajs/vue3'; import { ref } from 'vue'; const some = ref(null); const fields = ref(null); </script> <form method="POST" :action="route('report')"> <!-- 自动带入Inertia提供的CSRF令牌 --> <input type="hidden" name="_token" :value="page.props.csrf_token" /> <input name="some" v-model="some" /> <input name="fields" v-model="fields" /> <button type="submit">download</button> </form>
内容的提问来源于stack exchange,提问作者StR
相关产品推荐
相关产品推荐

