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
相关产品推荐
相关产品推荐

