Flutter中如何为URL请求添加Header以访问Token保护的PDF
问题:flutter_pdfview加载带Token认证的URL返回空PDF
我在用flutter_pdfview库从URL加载PDF,但该URL需要通过请求头携带Token(会话ID)才能访问。目前我的传参方式无效,获取到的是空PDF,以下是我的代码:
Future<File> createFileOfPdfUrl() async { Completer<File> completer = Completer(); if (kDebugMode) { print("Start download file from internet!"); } try { String url = "$customURL.pdf"; if (kDebugMode) { print("url: $url"); } final filename = url.substring(url.lastIndexOf("/") + 1); var client = HttpClient(); HttpClientRequest request = await client.getUrl(Uri.parse(url)); request.headers.add( HttpHeaders.acceptHeader, HeaderValue( "text/plain", {'APPAUTH': '${widget.authService.loginToken}'})); // 此处方式无效,得到空PDF var response = await request.close(); var bytes = await consolidateHttpClientResponseBytes(response); var dir = await getApplicationDocumentsDirectory(); if (kDebugMode) { print("Download files"); print("${dir.path}/$filename"); } File file = File("${dir.path}/$filename"); await file.writeAsBytes(bytes, flush: true); completer.complete(file); } catch (e) { throw Exception('Error parsing asset file!'); } return completer.future; }
解决方案
1. 修正请求头添加方式
你错误地把自定义Token放到了Accept请求头的参数里,正确的做法是单独添加自定义请求头:
// 替换原来的header添加代码 request.headers.add('APPAUTH', widget.authService.loginToken); // 同时设置正确的Accept头,告诉服务器你要接收PDF request.headers.add(HttpHeaders.acceptHeader, 'application/pdf');
2. 增加响应状态校验
添加响应状态码检查,避免下载到认证失败(如401)或错误的内容:
var response = await request.close(); if (response.statusCode != HttpStatus.ok) { throw Exception('请求失败,状态码: ${response.statusCode}'); }
3. 完整修正后的代码
Future<File> createFileOfPdfUrl() async { Completer<File> completer = Completer(); if (kDebugMode) { print("开始从网络下载文件!"); } try { String url = "$customURL.pdf"; if (kDebugMode) { print("url: $url"); } final filename = url.substring(url.lastIndexOf("/") + 1); var client = HttpClient(); HttpClientRequest request = await client.getUrl(Uri.parse(url)); // 正确添加自定义Token头 request.headers.add('APPAUTH', widget.authService.loginToken); // 设置接受PDF类型 request.headers.add(HttpHeaders.acceptHeader, 'application/pdf'); var response = await request.close(); // 校验响应状态 if (response.statusCode != HttpStatus.ok) { throw Exception('请求失败,状态码: ${response.statusCode}'); } var bytes = await consolidateHttpClientResponseBytes(response); // 调试:打印字节长度,确认是否获取到有效内容 if (kDebugMode) { print("下载字节长度: ${bytes.length}"); } var dir = await getApplicationDocumentsDirectory(); if (kDebugMode) { print("文件保存路径: ${dir.path}/$filename"); } File file = File("${dir.path}/$filename"); await file.writeAsBytes(bytes, flush: true); completer.complete(file); } catch (e) { throw Exception('处理文件时出错: $e'); } return completer.future; }
4. 替代方案:使用dio库简化下载
如果觉得原生HttpClient太繁琐,可以用dio库来处理,它对请求头和文件下载的支持更友好:
- 先在
pubspec.yaml添加依赖:
dependencies: dio: ^5.4.0+1 path_provider: ^2.1.2
- 实现代码:
import 'package:dio/dio.dart'; import 'package:path_provider/path_provider.dart'; Future<File> downloadPdfWithToken() async { Dio dio = Dio(); String url = "$customURL.pdf"; // 添加请求头 dio.options.headers['APPAUTH'] = widget.authService.loginToken; dio.options.headers['Accept'] = 'application/pdf'; var dir = await getApplicationDocumentsDirectory(); String savePath = "${dir.path}/${url.substring(url.lastIndexOf("/") + 1)}"; var response = await dio.download(url, savePath); if (response.statusCode != 200) { throw Exception('下载失败,状态码: ${response.statusCode}'); } return File(savePath); }
内容的提问来源于stack exchange,提问作者Florentin Lupascu
相关产品推荐
相关产品推荐

