我开始使用mime来解析电子邮件并提取附件。 我做的任何事情,当我把它写入磁盘时,二进制附件总是被破坏了。 然后我意识到,由于一些奇怪的原因,当消息被解析为数据类型时,所有base64附件都已被解码。那是我的问题开始的时候。
如果是图像,则不起作用。
我做的第一件事是将提取的Text附件转换为带有TE.encodeUtf8
的ByteString。
没运气。我尝试了所有Text.Encoding
函数将Text转换为ByteString - 没有任何效果。
然后由于一些愚蠢的原因我将提取的文本转换/编码回base64,然后我再次从base64解码它,这次它工作。为什么呢?
因此,如果我将提取的附件编码为base64并对其进行解码,则可以正常工作。
B.writeFile "tmp/test.jpg" $ B.pack $ decode $ encodeRawString True $ T.unpack attachment
为什么?为什么简单的Text to ByteString编码不起作用,但上面的愚蠢呢?
最终,我更多地玩了它,并且与Data.ByteString.Char8
这样的B.writeFile "tmp/test.jpg" $ BC.pack $ T.unpack attachment
一起使用时达到了这一点import Codec.MIME.Parse
import Codec.MIME.Type
import Data.Maybe
import Data.Text (Text, unpack, strip)
import qualified Data.Text as T (null)
import Data.Text.Encoding (encodeUtf8)
import Data.ByteString (ByteString)
data Attachment = Attachment { attName :: Text
, attSize :: Int
, attBody :: Text
} deriving (Show)
genAttach :: Text -> [Attachment]
genAttach m =
let prs v = if isAttach v
then [Just (mkAttach v)]
else case mime_val_content v of
Single c -> if T.null c
then [Nothing]
else prs $ parseMIMEMessage c
Multi vs -> concatMap prs vs
in let atts = filter isJust $ prs $ parseMIMEMessage m
in if null atts then [] else map fromJust atts
isAttach :: MIMEValue -> Bool
isAttach mv =
maybe False check $ mime_val_disp mv
where check d = if (dispType d) == DispAttachment then True else False
mkAttach :: MIMEValue -> Attachment
mkAttach v =
let prms = dispParams $ fromJust $ mime_val_disp v
Single cont = mime_val_content v
name = check . filter isFn
where isFn (Filename _) = True
isFn _ = False
check = maybe "" (\(Filename n) -> n) . listToMaybe
size = check . filter isSz
where isSz (Size _) = True
isSz _ = False
check = maybe "" (\(Size n) -> n) . listToMaybe
in Attachment { attName = name prms
, attSize = let s = size prms
in if T.null s then 0 else read $ unpack s
, attBody = cont
}
所以我仍然需要将Text转换为String,然后将String转换为ByteString.Char8,然后它才能正常工作,并且我会得到未损坏的图像。
可以请有人解释这一切。为什么这种痛苦与二进制图像附件? 为什么我不能将base64解码后的Text转换为ByteString?我错过了什么?
谢谢。
更新
这是根据请求提取附件的代码。 我认为它与文本编码/解码无关。
{{1}}
答案 0 :(得分:3)
请注意,mime包选择使用Text
值表示二进制内容。派生相应ByteString
的方法是对文本进行latin1编码。在这种情况下,保证文本字符串中的所有代码点都在0 - 255范围内。
使用以下内容创建文件:
Content-Type: image/gif
Content-Transfer-Encoding: base64
R0lGODlhAQABAIABAP8AAP///yH5BAEAAAEALAAAAAABAAEAAAICRAEAOw==
这是http://commons.wikimedia.org/wiki/File:1x1.GIF
的1x1红色GIF图像的base64编码以下是一些使用parseMIMEMessage
重新创建此文件的代码。
import Codec.MIME.Parse
import Codec.MIME.Type
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Data.ByteString.Char8 as BS
import System.IO
test1 path = do
msg <- TIO.readFile path
let mval = parseMIMEMessage msg
Single img = mime_val_content mval
withBinaryFile "out-io" WriteMode $ \h -> do
hSetEncoding h latin1
TIO.hPutStr h img
test2 path = do
msg <- TIO.readFile path
let mval = parseMIMEMessage msg
Single img = mime_val_content mval
bytes = BS.pack $ T.unpack img
BS.writeFile "out-bs" bytes
在test2
中,latin1编码由BS.pack . T.unpack
完成。
答案 1 :(得分:0)
mime
包始终使用Text
,但根据其base64
MIME撤消邮件部分正文的任何quoted-printable
(或Content-Encoding
)编码标题字段。
这意味着生成的消息部分的主体是Text
类型但是(除非MIME类型是text/*
)这不是一个合适的类型,因为正文应该是一个序列 bytes ,而不是字符。它使用的字符是Unicode代码点00
到FF
,它们具有明显的字节映射,但它们不是同一个东西。 (此外,如果MIME类型 text/*
但charset
不是us-ascii
或iso8859-1
,那么我认为mime
将会出现问题内容。)
我怀疑发生的事情是您正在将Text
写入您使用Data.Text.IO.writeFile
或类似的磁盘,该磁盘使用您环境中指定的字符编码将字符转换为字节。许多常见字符编码将字符00
映射到7F
到字节00
到7F
,但几乎没有将剩余字符80
映射到FF
到它们的Data.Bytestring.Char8
相应的字节。在如今的许多系统中,环境的编码是UTF8,它甚至不会将这些字符映射到单个字节。 (文件大小是否与您的预期不同?)
为了正确写出来,您需要先将这些字符转换为正确的字节。最简单的方法是使用String
模块中的函数,这些函数旨在以这种方式混淆字节和字符。但它们适用于Text
s而不是Text
s,因此您必须解压缩并重新包装所有内容。
我不确定你是如何设法对你的80
值进行base-64编码的,因为base-64编码也对字节而不是字符进行操作。无论你做了什么,你必须设法将字符FF
直接映射到{
"plugins": [
"transform-class-properties",
"babel-root-slash-import"
]
}
到相应的字节上,而不是以任何方式对它们进行编码。
如果您想了解更多信息,请参阅此处有一篇好文章:https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/