Golang服务器向Flutter发送大PDF显示空白问题排查
问题:Golang向Flutter传输大PDF文件显示空白的原因及解决方法
我需要通过HTTP从Golang服务器给Flutter应用发送PDF文件数据,目前的实现是:Golang读取PDF为字节后转成字符串发送,Flutter把字符串转成Uint8List再存储查看。但只有3KB以内的PDF能正常显示,10KB以上的都是空白。想问:为什么大文件不行?HTTP传输有大小限制吗?
原代码
原Golang代码
file, err := os.Open(filePath) if err != nil { ctx.JSON(http.StatusOK, gin.H{ "status": false, "error": err, }) } defer file.Close() // Get the file size stat, err := file.Stat() if err != nil { ctx.JSON(http.StatusOK, gin.H{ "status": false, "error": err, }) } // Read the file into a byte slice bs := make([]byte, stat.Size()) _, err = bufio.NewReader(file).Read(bs) if err != nil && err != io.EOF { ctx.JSON(http.StatusOK, gin.H{ "status": false, "error": err, }) } else { ctx.JSON(http.StatusOK, gin.H{ "status": true, "bytesdata": string(bs), }) }
原Flutter代码
final bytesdata = data['bytesdata']; final List<int> codeUnits = bytesdata.codeUnits; final Uint8List fileData = Uint8List.fromList(codeUnits); var dir1 = await getExternalStorageDirectory(); if (dir1 != null) { File file = File("${dir1.path}/$fileNameToView"); await file.writeAsBytes(fileData, flush: true); _redirectToPDFPage(file.readAsBytesSync(), fileList[index]["filename"]); }
核心原因
- 二进制转字符串导致数据丢失:PDF是二进制文件,直接把字节数组转成字符串(
string(bs))会破坏数据——字符串基于UTF-8编码,当字节包含UTF-8无法解析的序列时,Golang会自动替换为�(Unicode替换字符),Flutter还原时这些替换字符对应的字节和原PDF完全不一致,大文件包含更多这类非法序列,直接导致文件损坏。 - 文件读取不完整:
bufio.NewReader(file).Read(bs)无法保证一次性读取所有字节,Read方法仅会尽量读取数据但不填满整个切片,大文件更容易出现读取不全的情况,导致传输的字节本身就缺失。 - HTTP本身无大小限制:HTTP协议对传输数据的大小没有硬性限制,问题完全出在数据处理逻辑上,而非传输限制。
解决方法
推荐两种方案,优先选方案二(直接返回二进制流,更高效):
方案一:用Base64编码二进制数据
Base64会把二进制数据转成合法ASCII字符串,彻底避免编码丢失问题。
修改后的Golang代码
import ( "encoding/base64" "io/ioutil" ) file, err := os.Open(filePath) if err != nil { ctx.JSON(http.StatusOK, gin.H{"status": false, "error": err.Error()}) return // 错误后终止执行,避免后续逻辑出错 } defer file.Close() // 一次性读取所有字节,避免Read方法的不完整问题 bs, err := ioutil.ReadAll(file) if err != nil { ctx.JSON(http.StatusOK, gin.H{"status": false, "error": err.Error()}) return } // 转成Base64字符串返回 base64Str := base64.StdEncoding.EncodeToString(bs) ctx.JSON(http.StatusOK, gin.H{ "status": true, "bytesdata": base64Str, })
修改后的Flutter代码
import 'dart:convert'; final bytesdata = data['bytesdata']; // 解码Base64字符串还原二进制数据 final Uint8List fileData = base64Decode(bytesdata); var dir1 = await getExternalStorageDirectory(); if (dir1 != null) { File file = File("${dir1.path}/$fileNameToView"); await file.writeAsBytes(fileData, flush: true); _redirectToPDFPage(file.readAsBytesSync(), fileList[index]["filename"]); }
方案二:直接返回二进制文件流(更高效)
跳过字符串转码环节,直接返回PDF的二进制流,Flutter直接接收二进制响应。
修改后的Golang代码
file, err := os.Open(filePath) if err != nil { ctx.JSON(http.StatusOK, gin.H{"status": false, "error": err.Error()}) return } defer file.Close() // 设置正确的PDF内容类型 ctx.Header("Content-Type", "application/pdf") // 直接将文件内容写入响应 ctx.File(filePath)
修改后的Flutter代码
直接请求并接收二进制响应,无需字符串转换:
import 'dart:io'; import 'package:http/http.dart' as http; // 发起请求时直接获取二进制响应体 final response = await http.get(Uri.parse("你的API地址")); if (response.statusCode == 200) { final Uint8List fileData = response.bodyBytes; var dir1 = await getExternalStorageDirectory(); if (dir1 != null) { File file = File("${dir1.path}/$fileNameToView"); await file.writeAsBytes(fileData, flush: true); _redirectToPDFPage(file.readAsBytesSync(), fileList[index]["filename"]); } }
内容的提问来源于stack exchange,提问作者Dhananjay Gahiwade
相关产品推荐
相关产品推荐

