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

Yesod应用上传文件至临时文件夹:编码异常问题

问题分析与解决方案

问题根源

你遇到的乱码问题核心原因有两个:

  • 前端传递了完整的Data URL而非纯Base64内容:FileReader.readAsDataURL()返回的结果是带data:[<mediatype>];base64,前缀的完整Data URL字符串(比如data:text/plain;base64,Y29waWUgY2FydGUgZCdpZGVudGl0w6k=)。后端直接对整个字符串做Base64解码,前缀部分不属于Base64编码内容,解码后就会生成开头的乱码字符。
  • Haskell端字符串转ByteString的潜在风险:使用Data.ByteString.Char8.pack处理包含非ASCII字符的字符串时,会将字符截断为ASCII范围,不过这个问题在去掉Data URL前缀后会消失,因为纯Base64字符串仅包含ASCII字符。

修复步骤

1. 前端JavaScript修改

修改fileReader.onload中的逻辑,提取出纯Base64部分:

$("#file").on("change", function() {
    let file = this.files[0];
    let fileReader = new FileReader(); 
    fileReader.readAsDataURL(file);
    fileReader.onload = function() {
        // 分割Data URL,提取纯Base64内容
        let base64 = fileReader.result.split(',')[1];
        $.ajax({
            contentType: "application/json",
            processData: false,
            url: "@{FileR}",
            type: "PUT",
            data: JSON.stringify({
                _filename: file.name, 
                _base64: base64
            }),
            success: function(result) {
                // not important
            },
            dataType: "text"
        });
    }; 
    fileReader.onerror = function() {
        alert(fileReader.error);
    }; 
});

2. Haskell端优化(可选但更严谨)

虽然去掉前缀后原代码可以正常工作,但可以优化字符串转ByteString的方式,避免Data.ByteString.Char8的潜在问题,改用Data.ByteString.UTF8.fromString(需导入Data.ByteString.UTF8包):

import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.UTF8 as BU  -- 新增导入
import System.IO.Temp ( getCanonicalTemporaryDirectory )

base64ToFile :: String -> FilePath -> IO FilePath
base64ToFile b64string fileName = do
    -- 改用BU.fromString处理UTF-8字符串
    let bstring = B64.decodeLenient (BU.fromString b64string)
    tmpDir <- getCanonicalTemporaryDirectory
    let filePath = tmpDir ++ "/" ++ fileName
    B.writeFile filePath bstring 
    return filePath

-- 其余代码保持不变
data File = File {
    _filename :: String,
    _base64   :: String
} deriving (Show, Generic)

instance FromJSON File

b64FileToFile :: File -> IO FilePath
b64FileToFile file = base64ToFile (_base64 file) (_filename file)

putFileR :: Handler String
putFileR = do
    file <- requireCheckJsonBody :: Handler File
    liftIO $ b64FileToFile file 

验证效果

修改后上传原UTF-8文本文件,临时文件夹中的文件内容会和原文件完全一致:

copie carte d'identité
téléphone
autocertfication fiscale
signature

同时,该方案也支持CSV、XLSX、图片等二进制文件上传:readAsDataURL可以正确处理任意二进制文件,只要提取出纯Base64部分,后端解码后写入文件即可还原原文件内容。

内容的提问来源于stack exchange,提问作者Stéphane Laurent

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 04:00:35