24

I'm trying to use a local c# app to pull some images off a website to files on my local machine. I'm using the code listed below. I've tried both ASCII encoding and UTF8 encoding but the final file is not an correct. Does anyone see what I'm doing wrong? The url is active and correct and show the image just fine when I put the address in my browser.

    private void button1_Click(object sender, EventArgs e)
    {
        HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create("http://www.productimageswebsite.com/images/stock_jpgs/34891.jpg");

        // returned values are returned as a stream, then read into a string
        String lsResponse = string.Empty;
        HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse();
        using (StreamReader lxResponseStream = new StreamReader(lxResponse.GetResponseStream()))
        {
            lsResponse = lxResponseStream.ReadToEnd();
            lxResponseStream.Close();
        }

        byte[] lnByte = System.Text.UTF8Encoding.UTF8.GetBytes(lsResponse);

        System.IO.FileStream lxFS = new FileStream("34891.jpg", FileMode.Create);
        lxFS.Write(lnByte, 0, lnByte.Length);
        lxFS.Close();

        MessageBox.Show("done");
    }
Steven V
  • 16,357
  • 3
  • 63
  • 76
Steve Brownell
  • 1,310
  • 1
  • 12
  • 18
  • BTW, your HttpWebResponse and FileStream objects need to be in using blocks, since they implement IDisposable. Also, why initialize lsResponse, since it will always be set (or else an exception will be thrown). Finally, I suggest you stop using Hungarian notation (lxFS), you don't need a prefix in order to know the type of each object. Just hover the mouse over it. – John Saunders Mar 03 '10 at 01:05

4 Answers4

35

nice image :D

try using the following code:

you needed to use a BinaryReader, 'cause an image file is binary data and thus not encoded in UTF or ASCII

edit: using'ified

HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create(
"http://www.productimageswebsite.com/images/stock_jpgs/34891.jpg");

// returned values are returned as a stream, then read into a string
String lsResponse = string.Empty;
using (HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse()){
   using (BinaryReader reader = new BinaryReader(lxResponse.GetResponseStream())) {
      Byte[] lnByte = reader.ReadBytes(1 * 1024 * 1024 * 10);
      using (FileStream lxFS = new FileStream("34891.jpg", FileMode.Create)) {
          lxFS.Write(lnByte, 0, lnByte.Length);
      }
   }
}
MessageBox.Show("done");
Iman
  • 17,932
  • 6
  • 80
  • 90
Phil Rykoff
  • 11,999
  • 3
  • 39
  • 63
  • 1
    -1: I know you're just changing his provided code, but you should really improve it to not have resource leaks. Show him where to put the `using` blocks, and I'll remove my downvote. – John Saunders Mar 03 '10 at 01:25
  • I like using the .Close method instead of using blocks. According to msdn: This implementation of Close calls the Dispose method passing a true value. So we should be fine according to ressource leaks. are using blocks safer or have advantage over .Close()? (edit: forgot one close) – Phil Rykoff Mar 03 '10 at 01:32
  • 2
    @henchman: a `using` block ensures that `Dispose` will be called, even in the presence of an unhandled exception. It's a bad habit not to use them in almost all appropriate cases (where you create an instance of a class implementing `IDisposable`, only assign it to local variables, and don't return it from the method that created it, and when it's not a WCF proxy instance). If you always use it, you won't forget it once or twice. – John Saunders Mar 03 '10 at 02:15
  • 1
    thanks for the answers guys. I think John's right about the using blocks, even with the .close method. I think that does the dispose right away instead of disposing at the end of the routine when the variable falls out of scope. I also think I'll work on the ReadBytes part of the routine to read the stream in chunks until it ends instead of presuming a 10MB limit. – Steve Brownell Mar 03 '10 at 02:18
  • Please let us know about your solution for the 10MB limit - i couldn't find one for this type of reader/stream-combination. Stream.Length is not usable. Using-blocks also look nicer than .Close(), though it can be interesting when some more blocks will be aggregated regarding the indentation :-) – Phil Rykoff Mar 03 '10 at 02:25
  • Just want to make sure I'm following the byte math. 1 * 1024 * 1024 * 10 is meant as 1 byte times 1024 (kilobyte) times 1024 (megabyte) times 10 = 10 MB. But is there a reason we need the 1? Wouldn't 1024 * 1024 * 10 do the same thing? – bubbleking May 19 '16 at 15:13
  • Yes, of course. You could also multiply it out and write 10485760. It was probably meant to support the understanding of the reader without being crazy and using source code comments ;-) – Phil Rykoff May 19 '16 at 20:17
  • Need to drop lsResponse from the example. – Learner May 18 '17 at 07:50
15

Okay, here's the final answer. It uses a memorystream as a way to buffer the data from the reaponsestream.

    private void button1_Click(object sender, EventArgs e)
    {
        byte[] lnBuffer;
        byte[] lnFile;

        HttpWebRequest lxRequest = (HttpWebRequest)WebRequest.Create("http://www.productimageswebsite.com/images/stock_jpgs/34891.jpg");
        using (HttpWebResponse lxResponse = (HttpWebResponse)lxRequest.GetResponse())
        {
            using (BinaryReader lxBR = new BinaryReader(lxResponse.GetResponseStream()))
            {
                using (MemoryStream lxMS = new MemoryStream())
                {
                    lnBuffer = lxBR.ReadBytes(1024);
                    while (lnBuffer.Length > 0)
                    {
                        lxMS.Write(lnBuffer, 0, lnBuffer.Length);
                        lnBuffer = lxBR.ReadBytes(1024);
                    }
                    lnFile = new byte[(int)lxMS.Length];
                    lxMS.Position = 0;
                    lxMS.Read(lnFile, 0, lnFile.Length);
                }
            }
        }

        using (System.IO.FileStream lxFS = new FileStream("34891.jpg", FileMode.Create))
        {
            lxFS.Write(lnFile, 0, lnFile.Length);
        }
        MessageBox.Show("done");
    }
JKennedy
  • 18,150
  • 17
  • 114
  • 198
Steve Brownell
  • 1,310
  • 1
  • 12
  • 18
5

A variation of the answer, using async await for async file I/O. See Async File I/O on why this is important.

Download png and write to disk using BinaryReader/Writer

string outFile = System.IO.Path.Combine(outDir, fileName);

// Download file
var request = (HttpWebRequest) WebRequest.Create(imageUrl);

using (var response = await request.GetResponseAsync()){
    using (var reader = new BinaryReader(response.GetResponseStream())) {

        // Read file 
        Byte[] bytes = async reader.ReadAllBytes();

        // Write to local folder 
        using (var fs = new FileStream(outFile, FileMode.Create)) {
            await fs.WriteAsync(bytes, 0, bytes.Length);
        }
    }
}

Read all bytes extension method

public static class Extensions {

    public static async Task<byte[]> ReadAllBytes(this BinaryReader reader)
    {
        const int bufferSize = 4096;
        using (var ms = new MemoryStream())
        {
            byte[] buffer = new byte[bufferSize];
            int count;
            while ((count = reader.Read(buffer, 0, buffer.Length)) != 0) {
                await ms.WriteAsync(buffer, 0, count);
            }
            return ms.ToArray();
        }
    }
}
Learner
  • 2,459
  • 4
  • 23
  • 39
1

You can use the following method to download an image from a web site and save it, using the Image class:

WebRequest req = WebRequest.Create(imageUrl);
WebResponse resp = req.GetResponse();
Image img = Image.FromStream(resp.GetResponseStream());
img.Save(filePath +  fileName + ".jpg");
Rob
  • 26,989
  • 16
  • 82
  • 98
Tarek.Mh
  • 638
  • 10
  • 8