You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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"]);
}

核心原因

  1. 二进制转字符串导致数据丢失:PDF是二进制文件,直接把字节数组转成字符串(string(bs))会破坏数据——字符串基于UTF-8编码,当字节包含UTF-8无法解析的序列时,Golang会自动替换为�(Unicode替换字符),Flutter还原时这些替换字符对应的字节和原PDF完全不一致,大文件包含更多这类非法序列,直接导致文件损坏。
  2. 文件读取不完整:bufio.NewReader(file).Read(bs)无法保证一次性读取所有字节,Read方法仅会尽量读取数据但不填满整个切片,大文件更容易出现读取不全的情况,导致传输的字节本身就缺失。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 04:52:54