如何调整大小和图像并另存为字节数组

How to resize and image and save as byte array

我试图让用户 select 一个图像文件,然后根据需要将图像调整为几个不同的大小,然后将调整后的图像保存到字节数组中,这些字节数组将保存到数据库中。将图像转换为字节数组时,我不断收到错误消息(但前提是图像已调整大小)。希望有人能告诉我我做错了什么。

仅供参考,我不是专业程序员,当我 post 我的代码时通常会受到严厉批评。我绝对乐于接受您的任何反馈或建议,所以请保持友好..lol

我通常在 Image.Save(mStream, TheImage.RawFormat) 行的 SaveImage() 函数中遇到此错误 System.ArgumentNullException: 'Value cannot be null. Parameter name: encoder'

有时我会遇到框架错误,但无法真正判断错误发生在何处。

编辑:我最终扩展了图像 class,到目前为止它似乎工作得很好。

Imports System.Drawing.Imaging
Imports System.IO

Module Extentions
    <Runtime.CompilerServices.Extension()>
    Public Function GetBytes(ByRef bc As Image, Optional Format As ImageFormat = Nothing) As Byte()
        'Converts Image to Byte Array
        If Format Is Nothing Then Format = ImageFormat.Jpeg
        Using mStream As New MemoryStream()
            bc.Save(mStream, Format)
            Return mStream.ToArray()
        End Using
    End Function

    <Runtime.CompilerServices.Extension()>
    Public Function ReSize(ByRef bc As Image, MaxWidth As Integer, MaxHeight As Integer) As Image
        'Resize image to fit within MaxWidth & MaxHeight while keeping aspect ratio
        If MaxWidth < bc.Width Or MaxHeight < bc.Height Then
            Dim Scale As Double = Math.Min(MaxWidth / bc.Width, MaxHeight / bc.Height)
            Dim NewWidth As Integer = CInt(Math.Round(bc.Width * Scale))
            Dim NewHeight As Integer = CInt(Math.Round(bc.Height * Scale))
            Return New Bitmap(bc, New Size(NewWidth, NewHeight))
        Else
            Return bc
        End If
    End Function
End Module

现在,我以前处理的表单中的代码简单多了..

    Private Sub SelectImage_Click(sender As Object, e As EventArgs) Handles SelectImage.Click
        Dim Popup As New OpenFileDialog
        With Popup
            .Filter = "Images|*.gif;*.png;*.jpg;*.bmp"
            .Title = "Select Image"
            .FileName = ""
            .FilterIndex = 1
            .Multiselect = False
            .RestoreDirectory = True
        End With

        If Popup.ShowDialog = vbOK Then
            'Load Image from disk
            OriginalImage = Image.FromFile(Popup.FileName)

            'Resize and display image on form
            Picture.Image = OriginalImage.ReSize(200, 150)

            'Save Large Image
            FullImage = OriginalImage.ReSize(400, 300).GetBytes

            'Save Small Image
            SmallImage = OriginalImage.ReSize(100, 75).GetBytes

            ImageSelected = True
        End If
    End Sub

这是我的原始代码...

    Private OriginalImage As Image
    Private FullImage As Byte()
    Private SmallImage As Byte()
    Private ImageSelected As Boolean
Private Sub SelectImage_Click(sender As Object, e As EventArgs) Handles SelectImage.Click
        Dim Popup As New OpenFileDialog
        With Popup
            .Filter = "Images|*.gif;*.png;*.jpg;*.bmp"
            .Title = "Select Image"
            .FileName = ""
            .FilterIndex = 1
            .Multiselect = False
            .RestoreDirectory = True
        End With

        If Popup.ShowDialog = vbOK Then
            'Load Image from disk
            OriginalImage = Image.FromFile(Popup.FileName)

            'Resize and display image on form
            Picture.Image = ResizeImage(OriginalImage, 200, 150)

            'Save Large Image
            Dim ResizedImage As Image = ResizeImage(OriginalImage, 400, 300)
            FullImage = SaveImage(ResizedImage)

            'Save Small Image
            ResizedImage = ResizeImage(OriginalImage, 100, 75)
            SmallImage = SaveImage(ResizedImage)

            ImageSelected = True
        End If
    End Sub

    Private Function ResizeImage(ByRef InputImage As Image, MaxWidth As Integer, MaxHeight As Integer) As Image
        'ReSize the Image if needed to save space in the database
        If MaxWidth < InputImage.Width Or MaxHeight < InputImage.Height Then
            'ReSize Image
            Dim Scale As Double = Math.Min(MaxWidth / InputImage.Width, MaxHeight / InputImage.Height)
            Return New Bitmap(InputImage, New Size(Math.Round(InputImage.Width * Scale), Math.Round(InputImage.Height * Scale)))
        Else
            'Image size was OK
            Return InputImage
        End If
    End Function

    Private Function GetImage(ImageData As Byte()) As Image
        'Converts Byte Array to Image
        Using mStream As New MemoryStream(ImageData)
            Return Image.FromStream(mStream)
        End Using
    End Function

    Private Function SaveImage(TheImage As Image) As Byte()
        'Converts Image to Byte Array
        Using mStream As New MemoryStream()
            TheImage.Save(mStream, TheImage.RawFormat)
            Return mStream.ToArray()
        End Using
    End Function


    'These are functions I was playing around with because I was getting
    'System.ArgumentNullException: 'Value cannot be null. Parameter name: encoder'
    'in the SaveImage() function on TheImage.Save(mStream, TheImage.RawFormat)
    'and I wasn't using the encoder parameter. I couldn't get these to help me though.

    Private Function SaveImage2(TheImage As Image) As Byte()
        'Converts Image to Byte Array
        Dim CodecInfo As Imaging.ImageCodecInfo = GetEncoderInfo("image/jpeg")
        Dim Parameters As New Imaging.EncoderParameters(1)
        Parameters.Param(0) = New Imaging.EncoderParameter(Imaging.Encoder.Quality, 75L)
        Using mStream As New MemoryStream()
            TheImage.Save(mStream, CodecInfo, Parameters)
            Return mStream.ToArray()
        End Using
    End Function

    Private Function GetEncoderInfo(MimeType As String) As Imaging.ImageCodecInfo
        Dim encoders As Imaging.ImageCodecInfo()
        encoders = Imaging.ImageCodecInfo.GetImageEncoders()
        For j As Integer = 0 To encoders.Length - 1
            If encoders(j).MimeType = MimeType Then Return encoders(j)
        Next
        Return Nothing
    End Function

当您使用此构造函数创建新位图时:

new Bitmap([OriginalImage], [Width], [Height])

您可能认为考虑了 [OriginalImage] 格式。不是。将创建具有指定大小的新原始位图,并在新容器内绘制 [OriginalImage]
在内存中创建了新的Bitmap对象,其ImageFormatImageFormat.MemoryBmp.
这个 ImageFormat 没有 Encoder/Decoder。

当您使用传递新位图对象 RawFormatImage.Save(Stream, Format) 方法重载保存图像时,您传递的是 ImageFormat.MemoryBmp.
图像 class 然后尝试从指定的 ImageFormat 获取编码器(使用内部 FindEncoder() 方法),这当然是 returns null,导致异常:

System.ArgumentNullException: 'Value cannot be null. Parameter name: encoder'

你可以自己测试一下,尝试从OriginalImageResizedImage对象中获取Encoder/Decoder(使用ImageCodecInfo.GetImageDecoders() or ImageCodecInfo.GetImageEncoders()):

Dim originalCodec As ImageCodecInfo = ImageCodecInfo.GetImageDecoders().
    FirstOrDefault(Function(cc) cc.FormatID.Equals(ResizedImage.RawFormat.Guid))

您会看到,当您尝试获取 ResizedImage 格式的 Encoder/Decoder 时,originalCodec 将为空 (nothing),而它会return OriginalImage 格式的 Encoder/Decoder。

由于预定义的 ImageFormatImageFormat.Png,您可以更改将图像保存到 MemoryStream 时设置此格式的方法:

Using mStream As New MemoryStream()
    TheImage.Save(mStream, ImageFormat.Png)
    Return mStream.ToArray()
End Using

某件事在内部也不止一次做过。
例如:Image.Save(MemoryStream stream)