我有個困擾,Outlook 收到幾位特定同事寄來內嵌圖片的信,圖片會在回信或轉信後消失,出現叉燒包圖示及無法顯示連結的影像。檔案可能已移動、重新命名或刪除。請檢查連結是否指向正確的檔案及位置。/ The linked image cannot be displayed. The file may have been moved, renamed, or deleted. Verify that the link points to the correct file and location. 訊息。


(示意圖)

爬文發現我不孤獨,用「回信或轉信後圖片消失」可查到不少類似問題回報:

可惜這些案例是發生在雲端網頁版,而我是在本機 Office 2019 遇到,不同情境原因未必相同,解法也不適用。

摸摸鼻子,自己查看看好了。將信件另存成 .msg 檔進行解剖。

.msg 本質是個壓縮檔,解開裡面有資料夾及檔案,可用來儲存 Exchange 郵件、約會、連絡人等物件內容,用 7-Zip 檢視可以看到資料是以一個屬性一個檔案方式保存。(註:想深入細節的同學可以去啃 官方規格)

分析圖片消失的信件,找出問題所在。信件內嵌圖檔是以附件方式儲存,每個附檔會對映一個名為 __attach_version1.0_#0000000n 的資料夾,圖片消失的信件,其實圖檔都還在,只是這些圖片是以 <img width=217 height=222 style='width:2.2604in;height:2.3125in' id="圖片_x0020_2" src="cid:image001.png@01DAFF72.94F352E0"> HTML 標籤嵌入信件內文,而其中 image001.png@01DAFF72.94F352E0 是系統自動產生的 ContentId:

圖片附檔都在,但檢視時說連結未指向正確的檔案及位置,最容易的解釋是附檔的 cid:image001.png@... 跟 HTML 中的 src="cid:image001.png@..." 因不明原因對不上。

研究了一下,PowerShell 建立 Outlook.Application 後,可以用 CreateItemFromTemplate() 讀取 .msg,由 HtmlBody 取得 HTML 內容找出 src="cid:image001.png@...",列舉 Attachments 並用 Attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F") (PidTagAttachContentId 取得附件的 ContentId,比對二者是否有出入。

我寫了一支小程式檢查內文與附檔 CID 的一致性。

[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string]
    $msgPath
)
$ErrorActionPreference = 'STOP'
function Resolve-AbsPath($relPath) {
    return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($relPath)
}
$outlookApp = New-Object -ComObject Outlook.Application
if (!$outlookApp) {
    throw "無法開啟 Outlook 程式"
}
$fullPath = Resolve-Path $msgPath
Write-Host "開啟 $fullPath"
$mailItem = $outlookApp.CreateItemFromTemplate($fullPath)
$res = [regex]::Matches($mailItem.HtmlBody, 'src="cid:([^"]+)"')
Write-Host "共有 $($res.Count) 個內嵌圖片"
foreach ($match in $res) {
    $cid = $match.Groups[1].Value
    Write-Host "  $cid"
}

$attachments = $mailItem.Attachments
Write-Host "共有 $($attachments.Count) 個附件"
try {
    $attachments | ForEach-Object {
        $attachment = $_
        $cid = $attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F") 
        Write-Host "  [$cid]"
    }
}
finally {
    $mailItem.Delete()
}

分別檢查正常郵件 good.msg 及問題郵件 broken.msg,證實了問題出在 CID 對映:正常郵件 HTML 中的 CID 與附件的 CID 一致,而問題郵件兩邊的數量相符,但 CID 對不起來。證實「CID 錯亂導致圖片無法顯示」是問題根源,但原因不明,也有可能是 Outlook 的 Bug,恐會查到心累。

最後,我寫了一支 Workaround 小工具,將圖片附檔另存,至少能取回消失的圖片,並嘗試將 HTML 中的 CID 依序換成附檔目前的 CID 以修正問題。

[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string]
    $msgPath,
    [string]
    $attachmentFolder = '.'
)
$ErrorActionPreference = 'STOP'
function Resolve-AbsPath($relPath) {
    return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($relPath)
}
$outlookApp = New-Object -ComObject Outlook.Application
if (!$outlookApp) {
    throw "無法開啟 Outlook 程式"
}
$fullPath = Resolve-Path $msgPath
Write-Host "開啟 $fullPath"
$mailItem = $outlookApp.CreateItemFromTemplate($fullPath)
$res = [regex]::Matches($mailItem.HtmlBody, 'src="cid:([^"]+)"')
Write-Host "共有 $($res.Count) 個內嵌圖片"
$htmlCids = @()
foreach ($match in $res) {
    $cid = $match.Groups[1].Value
    $htmlCids += $cid
    Write-Host "  $cid"
}

$attachments = $mailItem.Attachments
Write-Host "共有 $($attachments.Count) 個附件"
$attCids = @()
try {
    $saveFolderPath = Resolve-AbsPath $attachmentFolder
    $attachments | ForEach-Object {
        $attachment = $_
        $cid = $attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001F") 
        $savePath = Join-Path $saveFolderPath $attachment.FileName
        Write-Host "  儲存[$cid] $savePath..."
        $attachment.SaveAsFile($savePath)
        if ($cid -match '^image\d+\.(png|jpg)@[0-9A-F]{8}\.[0-9A-F]{8}$') {
            $attCids += $cid
        }
    }
    $index = 0
    Write-Host "嘗試修正 CID"
    $htmlCids | ForEach-Object {
        $newCid = $attCids[$index]
        $index++
        Write-Host "  置換:$_ -> $newCid"
        $mailItem.HtmlBody = $mailItem.HtmlBody.Replace("cid:$_", "cid:$newCid")
    }
    $mailItem.SaveAs((Resolve-AbsPath 'try-fix.msg'))
}
finally {
    $mailItem.Delete()
}

實測成功,不但拿回消失的圖片,開啟郵件消失的圖片也重現了。讚~

This blog post addresses the issue of embedded images disappearing in Outlook emails when replying or forwarding. It explains the problem’s root cause related to ContentId mismatches and offers PowerShell scripts to diagnose and fix the issue by extracting and reassigning ContentIds for embedded images.


Comments

Be the first to post a comment

Post a comment