Related SO Answer(这是为什么?)
我正在调用一个webservice(我没有访问源代码),它返回给我一个字节数组,该数组应该代表一个多页TIFF图像。 (方法名称改为保留就业......)
var tiffBytes = _webServiceClient.GetTiff("someIdentifier");
如果我使用标准FileStream将tiffBytes
写入文件,然后打开生成的文件,知道什么是TIFF,没有问题,我可以正确浏览页面。当我直接从这个文件创建一个Image对象(Image.FromFile(...)
)时,我的其余代码也没有问题。这使我相信存储在tiffBytes
中的字节实际上是有效的。
如果我将tiffBytes
写入MemoryStream,事情会变得奇怪:
//Note that this code works just fine if I create "img" with Image.FromFile()
var frameCount = img.GetFrameCount(FrameDimension.Page); // frameCount = 2
img.SelectActiveFrame(FrameDimension.Page, 0); // First frame selected with no issue
img.SelectActiveFrame(FrameDimension.Page, 1); // Exception: "A generic error occured in GDI+"
我认为我错误地创建了我的MemoryStream,但我真的不认为我是。我尝试了几种不同的方法,结果相同;生成的Image对象将显示并正常运行,直到我尝试移动到最后一页:
方法1:
using(var ms = new MemoryStream(tiffBytes))
{
img = Image.FromStream(ms);
}
方法2:
using(var ms = new MemoryStream(tiffBytes.Length))
{
ms.Write(tiffBytes, 0, tiffBytes.Length);
img = Image.FromStream(ms);
}
所以...我的问题是为什么Image.FromFile()和Image.FromStream()之间的区别使得TIFF的最后一页/帧在加载时会导致异常?
附带问题...是否有某种win32 api可以访问以确切了解该异常是什么?
答案 0 :(得分:0)
Hans Passant的评论很有用,但我没有找到完整的解决方案。
尤其是当您打开多个文件并创建合并的输出时,图像处理非常麻烦。
我战斗了几天,有了一项功能,该功能可以使用现有的多页tif并附加其他页面。这些其他页面可以是单页tif,多页tif或图像文件。
输出将替换原始的多页tif,然后删除已附加的“新”文件(您可能不想删除源文件。我这样做是因为它是文档管理系统的一部分)。
打开/关闭文件时以及在代码中应分别执行操作以避免文件锁定时出现问题。
以下解决方案对我来说效果很好。这是用VB.net 4.6(注释显示为//,因为VB注释“格式不正确”)使用老式的win形式编写的,尽管此代码位于业务层对象中。
请注意每个文件的打开以及“使用”块的使用。如果您尝试对Using块之外的文件执行任何操作,则会遇到麻烦。即使我将代码将每个页面图像加载到图像列表中,如果我在创建最终合并输出的代码块之前放置End,也无法访问图像页面。
Public Shared Function AppendPage(OriginalDocPath As String, NewPageFilePath As String) As Boolean
//Define return value, default as fail
Dim bRet As Boolean = False
Try
//get the codec for tiff file
Dim info As ImageCodecInfo = Nothing
Dim ice As ImageCodecInfo
//find TIF in the encoder list
For Each ice In ImageCodecInfo.GetImageEncoders()
If ice.MimeType = "image/tiff" Then
info = ice
End If
Next ice //use the 'save' encoder
//List to store all output pages
Dim imageList As New List(Of Image)
Dim frameCount As Integer = 0
//Set up the 'Save' encoder for multi page tif
Dim enc As Encoder = Encoder.SaveFlag
Dim ep As New EncoderParameters(1)
ep.Param(0) = New EncoderParameter(enc, CLng(EncoderValue.MultiFrame))
//control for original tif that I want to append to
Dim oldDoc As Image
Using fs As Stream = File.OpenRead(OriginalDocPath)
//read the image file from a Stream
oldDoc = Bitmap.FromStream(fs)
//count the pages
frameCount = oldDoc.GetFrameCount(FrameDimension.Page)
Dim objGuid As Guid = (oldDoc.FrameDimensionsList(0))
Dim objDimension As FrameDimension = New FrameDimension(objGuid)
//load each page into the Image List
For i As Integer = 0 To frameCount - 1 Step 1
oldDoc.SelectActiveFrame(objDimension, i)
Dim nextFrame As Bitmap = New Bitmap(oldDoc)
imageList.Add(nextFrame)
Next
//the End Using will close the first image file
End Using
//variables for the appending image file
Dim newDoc As Image
Dim bmpFullDoc As Bitmap
//Open file to append
Using nStr As Stream = File.OpenRead(NewPageFilePath)
//read using a Stream
newDoc = Bitmap.FromStream(nStr)
//Count the pages in the image (works for tif, jpg, png)
frameCount = newDoc.GetFrameCount(FrameDimension.Page)
Dim objGuid As Guid = (newDoc.FrameDimensionsList(0))
Dim objDimension As FrameDimension = New FrameDimension(objGuid)
//add each page to the Image List
For i As Integer = 0 To frameCount - 1 Step 1
newDoc.SelectActiveFrame(objDimension, i)
Dim nextFrame As Bitmap = New Bitmap(newDoc)
imageList.Add(nextFrame)
Next
//make a backup of the original file
File.Copy(OriginalDocPath, OriginalDocPath & "-old", True)
//delete the original file
File.Delete(OriginalDocPath)
Dim firstFrame As Boolean = True
//create new file with all pages
For Each im As Image In imageList
//for the first page we need to actually create the file
If firstFrame Then
//add the first image to the output
bmpFullDoc = im
//save the first frame
bmpFullDoc.Save(OriginalDocPath, info, ep)
Else
//for subsequent pages we need to change the Encoder 'Page'
ep.Param(0) = New EncoderParameter(enc, CLng(EncoderValue.FrameDimensionPage))
//append next page
bmpFullDoc.SaveAdd(im, ep)
End If
//tell the loop we've saved the first page already
firstFrame = False
Next
End Using
//destroy the temp bitmap object
bmpFullDoc.Dispose()
//destroy the encoder parameters object
ep.Dispose()
//finally, delete the temp and source files
File.Delete(OriginalDocPath & "-old")
File.Delete(NewPageFilePath)
//Set Return value to Success
bRet = True
Catch ex As Exception
//Log error or throw exception
End Try
//return success or fail
Return bRet
End Function