tags:

views:

190

answers:

3

Hi.

I am trying to check if a file is an image before I upload it to the image server. I am doing it with the following function, which works exceptionally well:

static bool IsValidImage(Stream imageStream)
            {
                bool isValid = false;
                try
                {
                    // Read the image without validating image data
                    using (Image img = Image.FromStream(imageStream, false, false))
                    {
                        isValid = true;
                    }
                }
                catch
                {
                    ;
                }
                return isValid;
            }

The problem is that when the below is called immediately afterwards, The line:

while ((bytesRead = request.FileByteStream.Read(buffer, 0, bufferSize)) > 0)

evalueates to zero and no bytes are read. I notice that when I remove the IsValidImage function, bytes are read and the file is written. It seems that bytes can only be read once? Any idea how to fix this?

using (FileStream outfile = new FileStream(filePath, FileMode.Create))
                    {
                        const int bufferSize = 65536; // 64K
                        int bytesRead = 0;

                        Byte[] buffer = new Byte[bufferSize];

                        while ((bytesRead = request.FileByteStream.Read(buffer, 0, bufferSize)) > 0)
                        {
                            outfile.Write(buffer, 0, bytesRead);
                        }

                        outfile.Close(); //necessary?

                    }


UPDATE: Thanks for your help Marc. I am new to stream manipulation and could use a little more help here. I took a shot but may be mixing up the use of filestream and memorystream. Would you mind taking a look? Thanks again.

using (FileStream outfile = new FileStream(filePath, FileMode.Create))
using (MemoryStream ms = new MemoryStream())
{

   byte[] buffer = new byte[1024];
   int bytesRead;

   while ((bytesRead = request.FileByteStream.Read(buffer, 0, buffer.Length)) > 0)
   {
         ms.Write(buffer, 0, bytesRead);
   }

   // ms now has a seekable/rewindable copy of the data

   // TODO: read ms the first time

   // I replaced request.FileByteStream with ms but am unsure about 
   // the using statement in the IsValidImage function.

   if (!IsValidImage(ms) == true)
   {
        ms.Close();
        request.FileByteStream.Close();
        return;
   }

   ms.Position = 0;

   // TODO: read ms the second time

   byte[] m_buffer = new byte[ms.Length];
   while ((bytesRead = ms.Read(m_buffer, 0, (int)ms.Length)) > 0)
   {
        outfile.Write(m_buffer, 0, bytesRead);
   }


 } 


 static bool IsValidImage(MemoryStream imageStream)
 {
            bool isValid = false;
            try
            {
                // Read the image without validating image data
                using (Image img = Image.FromStream(imageStream, false, false))
                {
                    isValid = true;
                }
            }
            catch
            {
                ;
            }
            return isValid;
    }
+4  A: 

As you read from any stream, the position increases. If you read a stream to the end (as is typical), and then try to read again, then it will return EOF.

For some streams, you can seek - set the Position to 0, for example. However, you should try to avoid relying on this as it is not available for many streams (especially when network IO is involved). You can query this ability via CanSeek, but it would be simpler to avoid this - partly as if you are branching based on this, you suddenly have twice as much code to maintain.

If you need the data twice, then the options depends on the size of the data. For small streams, buffer it in-memory, as either a byte[] or a MemoryStream. For larger streams (or if you don't know the size) then writing to a scratch file (and deleting afterwards) is a reasonable approach. You can open and read the file as many times (in series, not in parallel) as you like.


If you are happy the stream isn't too large (although maybe add a cap to prevent people uploading swap-files, etc):

using (MemoryStream ms = new MemoryStream()) {
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0) {
        ms.Write(buffer, 0, bytesRead);
    }

    // ms now has a seekable/rewindable copy of the data

    // TODO: read ms the first time
    ms.Position = 0;
    // TODO: read ms the second time
}
Marc Gravell
Thanks Marc, my response in my question.
Code Sherpa
Thanks Mac - this is sorted now.
Code Sherpa
A: 

Indeed Stream instances remember where the current "cursor" is. Some streams support "rewinding". The "CanSeek" property will then return true. In the case of a HTTP request Ithis won't work (CanSeek = false).

Isn't a MIME-type sent from the browser as well?

If you really want to keep your way of checking you'll have to go with Marc's proposition

flq
Thanks Frank. Well, I am not married to my way of checking. I like that it is simple and, in test, seems pretty bullet-proof. But, I am not a huge fan of creating a scratch file to do write/reads in order to check for type validity. Could you suggest another method that checks for images (gif, tiff, png, jpg, bmp) without having to write to a temp file?
Code Sherpa
A: 

In your update, you have a problem reading the stream a second time.

byte[] m_buffer = new byte[ms.Length];
while ((bytesRead = ms.Read(m_buffer, 0, (int)ms.Length)) > 0)
{
    outfile.Write(m_buffer, 0, bytesRead);
}

The solution is simple:

byte[] m_buffer = ms.ToArray();
outfile.Write(m_buffer, 0, m_buffer.Length);

See also MemoryStream.ToArray

configurator
Thanks configurator, saved me a bunch of time there. Much appreciated.
Code Sherpa