4

I'm using a function that uploads an image, takes the stream and resizes it using imageresizer.net, then uploads the stream to Amazon S3.

Now I want to take a local picture and convert it into a stream. (to resize and upload to amazonS3). Basically, how do you convert an image into a stream.

This might be a simple question, just could not find the answer anywhere.

Here is some basic code.

Public Shared Sub MoveToAmazon(strImg As String, SKU As String)
        Dim fullImg As String = "C:\ImageLocation\" & strImg
        Dim img As Image = Image.FromFile(fullImg)

        'Here Im missing the code to convert it to a stream.
        UploadImage(imgStream, SKU)  

End Sub


Public Shared Sub UploadImage(imgStream As Stream, imgName As String)

    Dim MainStream As Stream = New MemoryStream
    Dim HomeStream As Stream = New MemoryStream
    Dim SmallStream As Stream = New MemoryStream
    Dim TinyStream As Stream = New MemoryStream
    Dim MidStream As Stream = New MemoryStream
    Dim GridStream As Stream = New MemoryStream
    Dim ListStream As Stream = New MemoryStream


    Dim c As New ImageResizer.Configuration.Config

    Dim SourceImage As Bitmap = New Bitmap(imgStream)
    Dim SourceMain As Bitmap = New Bitmap(SourceImage)
    Dim SourceHome As Bitmap = New Bitmap(SourceImage)
    Dim SourceSmall As Bitmap = New Bitmap(SourceImage)
    Dim SourceTiny As Bitmap = New Bitmap(SourceImage)
    Dim SourceMid As Bitmap = New Bitmap(SourceImage)
    Dim SourceGrid As Bitmap = New Bitmap(SourceImage)
    Dim SourceList As Bitmap = New Bitmap(SourceImage)

    ImageResizer.ImageBuilder.Current.Build(SourceMain, MainStream, New ResizeSettings("width=300&height=372&scale=both&paddingWidth=40")) 'ProductPage
    ImageResizer.ImageBuilder.Current.Build(SourceHome, HomeStream, New ResizeSettings("width=112&height=147&scale=both")) 'HomePage Products
    ImageResizer.ImageBuilder.Current.Build(SourceGrid, GridStream, New ResizeSettings("width=149&height=149&scale=both")) 'Categories Grid
    ImageResizer.ImageBuilder.Current.Build(SourceList, ListStream, New ResizeSettings("width=171&height=206&scale=both")) 'Categories List
    ImageResizer.ImageBuilder.Current.Build(SourceSmall, SmallStream, New ResizeSettings("width=64&height=75&scale=both")) 'Accessories
    ImageResizer.ImageBuilder.Current.Build(SourceTiny, TinyStream, New ResizeSettings("width=82&height=82&scale=both")) 'Cart
    ImageResizer.ImageBuilder.Current.Build(SourceMid, MidStream, New ResizeSettings("width=155&height=116&scale=both")) 'CategoryMain


    AmazonUploadFile("OriginalImages/" & imgName, imgStream)
    AmazonUploadFile("MainImages/" & imgName, MainStream)
    AmazonUploadFile("HomeImages/" & imgName, HomeStream)
    AmazonUploadFile("GridImages/" & imgName, GridStream)
    AmazonUploadFile("ListImages/" & imgName, ListStream)
    AmazonUploadFile("SmallImages/" & imgName, SmallStream)
    AmazonUploadFile("TinyImages/" & imgName, TinyStream)
    AmazonUploadFile("MidImages/" & imgName, MidStream)
End Sub

Public Shared Sub AmazonUploadFile(S3Key As String, FileStream As Stream)
    Dim request As New PutObjectRequest()
    request.WithBucketName(BUCKET_NAME)
    request.WithKey(S3Key).InputStream = FileStream
    request.WithCannedACL(S3CannedACL.PublicRead)
    GetS3Client.PutObject(request)
End Sub
Lilith River
  • 16,204
  • 2
  • 44
  • 76
monsey11
  • 243
  • 4
  • 18

3 Answers3

5

[Disclaimer - I'm the author of the ImageResizing.NET library the OP is asking the question about.]

Folks - do NOT use Bitmap and Image instances if you can possibly avoid it. There is a giant list of pitfalls that will crash your server. Do NOT use ANYTHING from System.Drawing without a server-safe wrapper around it.

@dash - Your code is almost right, aside from the memory leaks.

Decoding and encoding images safely isn't straightforward. Let the ImageResizing.Net library handle it.

Dim settings as New ResizeSettings("width=64&height=75&scale=both")
Using ms As New MemoryStream()
    ImageBuilder.Current.Build("C:\ImageLocation\" & strImg, ms, settings)
    ms.Seek(0, SeekOrigin.Begin) 
    UploadImage(ms, SKU)
End Using

Never load something into a Bitmap or Image instance if you're making multiple versions. Clone the file into a MemoryStream instead.

Using fs as New FileStream(...)
  Using ms as MemoryStream = Util.StreamUtils.CopyStream(fs)
     'For loop here with your setting variations
     ms.Seek(0, SeekOrigin.Begin)
     'Place upload and resize code here
     'End Loop
  End Using
End Using
Lilith River
  • 16,204
  • 2
  • 44
  • 76
  • He's not resizing locally, he's using a third party service to resize the image. The question is about how to place the image in a stream. The fact that the stream is wrapped in using should handle the dispose of resources so it should not leak. Also note that we've been resizing images locally on our own server for almost 5 years using an approach similar to the one in the SO question I linked to, and it has never errored, so we'll have to agree to disagree! However, point 5) on the list you post is quite interesting. – dash Apr 04 '12 at 14:53
  • You should probably also mention you are the author of the library you link to. Looks good though :-) – dash Apr 04 '12 at 14:57
  • @dash **What third-party service?** He's using my library, http://imageresizing.net. It's a locally-executed library. The stream isn't what you should worry about. MemoryStream instances in .NET 1-4 don't even **do** anything when they're disposed - they just wait for GC. What you need to be **scared to death** of is letting that Image or Bitmap instance go without it being in a using() clause. They appear to be 10K to the GC, so it procrastinates until your worker process recycles from memory limits, which can happen every 500ms under load. – Lilith River Apr 04 '12 at 15:03
  • **Your** third party library - I only realised it was yours after linking through to the main site! I see you advocate loading the image using a file stream rather than via the Image class which is the only thing out of a using in my example. I'll wrap that :-) – dash Apr 04 '12 at 15:08
  • Okay, Okay! I put a disclaimer up there. Still not sure why it's relevant, and I'd rather my answer be respected based on it's logic rather than based on my 5 years in the server-side image processing 'field'. – Lilith River Apr 04 '12 at 15:14
  • I've +1'd your answer for the great link :-) – dash Apr 04 '12 at 15:20
  • It removes any conflict of interest someone may accuse you of, which then allows your very good post to stand on it's own merits, which are significant. – dash Apr 04 '12 at 15:24
3

The following code snippet should do what you want:

  Using myImage = Image.FromFile(fullImg)
    Using ms As New MemoryStream()
        myImage.Save(ms, ImageFormat.Jpeg)
        ms.Seek(0, SeekOrigin.Begin) 
        UploadImage(ms, SKU)  
    End Using
  End Using

As an aside, you might find it easier to parameterize your methods and do all the work when calling them. Something like the following may make your life easier (this assumes the code you posted is code you are actually using and not a demo):

Public Shared Sub UploadImages()
    'Call this for each image
    MoveToAmazon("C:\ImageLocation\blah.jpg", "OriginalImage", 300, 300, 0, "whatever")

End Sub


Public Shared Sub MoveToAmazon(strImg As String, targetFolder As String, height as Integer, width as Integer, padding as Integer, SKU As String)
        Dim fullImg As String = "" & strImg
        Using img = Image.FromFile(fullImg)
            'Here Im missing the code to convert it to a stream.
            Using ms As New MemoryStream()
                Image.Save(ms, ImageFormat.Jpeg)
                ms.Seek(0, SeekOrigin.Begin) 
                UploadImage(ms, SKU)  
            End Using
        End Using
End Sub


Public Shared Sub UploadImage(imgStream As Stream, imgName As String, targetFolder As String, height as Integer, width as Integer, padding as Integer, SKU As String)

    Dim c As New ImageResizer.Configuration.Config

    ImageResizer.ImageBuilder.Current.Build(SourceMain, imgStream, New ResizeSettings("width=" & CStr(width) & "&height=" & CStr(height) & "&scale=both&paddingWidth=" & CStr(padding)) 

    AmazonUploadFile(targetFolder & "/" & imgName, imgStream)

End Sub

Public Shared Sub AmazonUploadFile(S3Key As String, FileStream As Stream)
    Dim request As New PutObjectRequest()
    request.WithBucketName(BUCKET_NAME)
    request.WithKey(S3Key).InputStream = FileStream
    request.WithCannedACL(S3CannedACL.PublicRead)
    GetS3Client.PutObject(request)
End Sub


Using ms As New MemoryStream()
    Image.Save(ms, ImageFormat.Jpeg)
    ms.Seek(0, SeekOrigin.Begin) 
    UploadImage(ms, SKU)  
End Using
dash
  • 89,546
  • 4
  • 51
  • 71
  • where is the function uploadedImage from? – monsey11 Apr 02 '12 at 13:46
  • I'm new to .net and streams. can i save a .jpg image to a Bitmap? I'm also using the imageresizer to add white space to images. can it all be done locally? – monsey11 Apr 02 '12 at 13:51
  • Yes; to answer your first question, although the object is called Bitmap, it actually represents an image. So you would use System.Drawing.Imaging.ImageFormat.Jpeg - see http://msdn.microsoft.com/en-us/library/system.drawing.imaging.imageformat.aspx – dash Apr 02 '12 at 14:06
  • For your second question; yes you can, but if you aren't confident enough about this, continue to use ImageResizer.net. If you do want to try it yourself, see http://stackoverflow.com/questions/2555916/c-sharp-image-whitespace for an example. – dash Apr 02 '12 at 14:07
  • uploadedImage would be the name of the image you are working with; this could be a file you have uploaded from your website, for example - since you didn't provide any example code, I gave you a generic example. – dash Apr 02 '12 at 14:08
  • @dash - Using the Bitmap constructor to resize an image will result it a very low-quality image (nearest-neighbor interpolation is used instead of 2-pass bicubic), and will not support transparency reliably. Also, encoding as a Bitmap may not be optimal. And encoding as Jpeg without specifying (and properly disposing) the encoding parameters will result in an absurdly large file. quality = 90L is best. Also, please fix the memory leaks before some poor newbie copies and pastes your code.... – Lilith River Apr 04 '12 at 15:09
  • Have wrapped the image in a using, but the stream is wrapped in a using and so should be disposed correctly. I don't believe the OP is doing anything other than then passing the stream to your library so I'm confident it will be safe after that! – dash Apr 04 '12 at 15:23
  • You might consider simplifying your examples - the last code block seems sufficient to me. – Lilith River Apr 04 '12 at 16:05
2

Read the image bytes and then you wrap it in a MemoryStream

MemoryStream ms = new MemoryStream(imageBytes);
Icarus
  • 63,293
  • 14
  • 100
  • 115
  • Typo: says MemoryStrean in code, note n at end, in second instance in code ;) Probably should be MemoryStream. ;) – Zeek2 Jun 27 '22 at 10:51