如何解决Flutter中API调用时出现的308重定向错误?
解决Flutter中MultipartRequest遇到308重定向无法获取图片的问题
问题核心
你遇到的问题根源在于:308重定向后不需要再次上传图片文件,服务器返回的Location URL是直接获取生成后图片的地址,应该用GET请求访问,而不是重复发送带文件的POST请求。Postman会自动处理这种重定向逻辑,但Flutter的http.MultipartRequest不会自动跟随重定向,而你手动处理时错误地重复上传了文件,导致服务器返回重定向页面而非图片。
修复步骤
- 收到308/301/302状态码时,从响应头的
location字段提取目标URL - 对该URL发送GET请求,无需再添加任何文件或表单数据
- 处理相对路径的重定向URL:如果
location返回的是相对路径,需要和原请求的Base URL拼接
修改后的代码
Future<void> tryOn() async { setState(() { _isLoading = true; }); try { if (selectedImageFile == null || selectedSourceFile == null) { showDialog( context: context, builder: (context) => CustomDialog( headline: "未选择文件", subline: "请选择两张图片。", red: () {}, green: () {}, redButton: "", greenButton: "", animation: "assets/animations/inf.json", ), ); return; } final uri = Uri.parse(urlText.text); var request = http.MultipartRequest('POST', uri); request.files.add(await http.MultipartFile.fromPath( 'profile_image', selectedImageFile!.path, )); request.files.add(await http.MultipartFile.fromPath( 'source_image', selectedSourceFile!.path, )); request.headers['Accept'] = 'application/json'; final streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); log("响应状态码: ${response.statusCode}"); log("响应体: ${response.body}"); // 处理重定向 if ([301, 302, 308].contains(response.statusCode)) { final redirectedUrl = response.headers['location']; if (redirectedUrl != null) { log("重定向到: $redirectedUrl"); Uri redirectedUri; // 处理相对路径的重定向URL if (redirectedUrl.startsWith('http')) { redirectedUri = Uri.parse(redirectedUrl); } else { redirectedUri = uri.resolve(redirectedUrl); } // 发送GET请求获取图片,无需再传文件 final imageResponse = await http.get(redirectedUri); log("重定向响应状态码: ${imageResponse.statusCode}"); if (imageResponse.statusCode == 200) { Uint8List resultImage = imageResponse.bodyBytes; showDialog( context: context, builder: (context) => Dialog( child: Image.memory(resultImage), ), ); } else { showDialog( context: context, builder: (context) => CustomDialog( headline: "失败", subline: "服务器返回: ${imageResponse.body}", red: () {}, green: () {}, redButton: "", greenButton: "", animation: "assets/animations/inf.json", ), ); } } } else if (response.statusCode == 200) { Uint8List resultImage = response.bodyBytes; showDialog( context: context, builder: (context) => Dialog( child: Image.memory(resultImage), ), ); } else { showDialog( context: context, builder: (context) => CustomDialog( headline: "失败", subline: "服务器返回: ${response.body}", red: () {}, green: () {}, redButton: "", greenButton: "", animation: "assets/animations/inf.json", ), ); } } on SocketException catch (e) { log("Socket异常: $e"); showDialog( context: context, builder: (context) => CustomDialog( headline: "网络错误", subline: "发生网络错误: $e", red: () {}, green: () {}, redButton: "", greenButton: "", animation: "assets/animations/inf.json", ), ); } on http.ClientException catch (e) { log("客户端异常: $e"); showDialog( context: context, builder: (context) => CustomDialog( headline: "客户端错误", subline: "发生客户端错误: $e", red: () {}, green: () {}, redButton: "", greenButton: "", animation: "assets/animations/inf.json", ), ); } catch (e) { log("异常: $e"); showDialog( context: context, builder: (context) => CustomDialog( headline: "失败", subline: "$e", red: () {}, green: () {}, redButton: "", greenButton: "", animation: "assets/animations/inf.json", ), ); } setState(() { _isLoading = false; }); }
额外说明
- 308状态码属于永久重定向,虽要求保留原请求方法,但在这个场景中,服务器的重定向实际指向生成后的图片资源,因此用GET请求即可
- 使用
uri.resolve(redirectedUrl)可以自动拼接原URL的Base路径,避免无效的相对路径请求
内容的提问来源于stack exchange,提问作者Haroon
相关产品推荐
相关产品推荐

