.NET Framework - HttpPostedFileBase and byte[]

Asked By shapper on 16-Sep-09 12:01 AM
Hello,

I am trying to implement a new class based on
System.Web.HttpPostedFileBase so I can create a two directional
mapping between byte[] File and HttpPostedFileBase. So I have the
following:

public class HttpFile : HttpPostedFileBase, IDisposable {

public override int ContentLength { get { return (int)
this.InputStream.Length; } }

public override string ContentType { get { return
this._ContentType; } set { _ContentType = value; } }
private string _ContentType;

public override string FileName { get { return base.FileName; }
set { _FileName = value; } }
private string _FileName;

public override Stream InputStream {
get {
if (_Stream == null) {
_Stream = new FileStream(_FileName, FileMode.Open,
FileAccess.Read, FileShare.Read);
}
return _Stream;
}
}
private FileStream _Stream;

public HttpFile(string fileName, string contentType) {
this._ContentType = contentType;
this._FileName = fileName;
} // HttpFile

public void Dispose() {
if (_Stream != null) {
try { _Stream.Dispose(); } finally { _Stream = null; }
}
} // Dispose

public override void SaveAs(String filename) {
File.WriteAllBytes(filename, File.ReadAllBytes(_FileName));
} // SaveAs
}

I need to be able to create an HttpFile where its stream is taken from
a byte[] object.

How can I do this?

I am a little bit confused about this.

Note: in ContentType and FileName properties I added the "set" code.
I am not sure if I should or can do this in this case.

Thanks,
Miguel




shapper replied on 16-Sep-09 12:01 AM
I cannot add the set property to properties as I mentioned before.

And to create an HttpFile from a byte array I created the following
constructor:

public HttpFile(String fileName, String contentType, byte[]
contentData) {
this._ContentType = contentType;
this._FileName = fileName;
this._Stream = new FileStream(fileName, FileMode.Open,
FileAccess.Read, FileShare.Read);
_Stream.Write(contentData, 0, contentData.Length);
_Stream.Close();
} // HttpFile

I am not sure if this is the way to do this ... Is it?

Thanks,
Miguel
Patrice replied on 10-Sep-09 12:29 PM
Do you need a file stream ? You could perhaps use a stream that does not save
to disk such as a MemoryStream. Allso IMO the stream should not be closed or
at the end so that a consumer could read the file content...

IMO you should start by explaining what is the overall goal. In particular
if you already have the file content as a byte array I am not sure why you
would want to create a class to show this as a HttpPostedFile or tell us
what is the cntext in which you would use your class...

If you just want to get the file data as a byte array this is something
already available with HttpPostedFile(s) and perhaps an extension method
would be enough to do posisbly some custom processing on it...


--
Patrice

discussion :
1a019b55-6775-4b14-a31c-755a638a2e9f@g19g2000yqo.googlegroups.com...
Jesse Houwing replied on 10-Sep-09 01:54 PM
* Patrice wrote, On 10-9-2009 18:29:

I agree. A MemoryStream would be the ideal solution. You can initialize
it with a byte array and it works just like a filestream after that. The
only possible problem is that it will stay in memory for the full
lifetime of the object. If these files are not too big and if you do not
have too many of these objects alive at the same time, that should not
be a problem.

Something else you will need to look into is the IDisposable interface.
If you are going to keep a stream in memory, or for that fact, keep a
stream to a File as a field or property of your class, then you need to
ensure that you correctly close the stream and clean up after you are
done with it. You've done it as many developers would do, but not as the
Framework guidelines describe it.

See the altered code at the bottom of this post.

Kind Regards,

Jesse Houwing






using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.IO;
;

namespace ConsoleApplication1
{
public class HttpFile : HttpPostedFileBase, IDisposable
{

public override int ContentLength
{
get
{
if (InputStream == null)
{
return 0;
}
else {
long length = InputStream.Length;
if (length > int.MaxValue)
{
return int.MaxValue;
}
else
{
return (int)length;
}
}
}
}

public override string ContentType
{
get
{
return
this._ContentType;
}
set { _ContentType = value; }
}
private string _ContentType;

public override string FileName
{
get { return base.FileName; }
set { _FileName = value; }
}
private string _FileName;

public override Stream InputStream
{
get
{
if (_Stream == null)
{
if (File.Exists(_FileName))
{
_Stream = new FileStream(_FileName, FileMode.Open,
FileAccess.Read, FileShare.Read);
shapper replied on 16-Sep-09 12:01 AM
On a ASP.NET MVC application when a file is uploaded I have on my
ViewModel a property of type HttpPostedFileBase:
http://msdn.microsoft.com/en-us/library/system.web.httppostedfilebase.aspx

The binder fills this property with the uploaded file.

I am using two directional mapping between the ViewModel and the
Entity where the file property is of type byte[];

So I need to create this conversion in the two directions ...
shapper replied on 16-Sep-09 12:01 AM
/en-us/library/system.web.httppostedfilebase...

I have been looking at this and basically my question is:

How to write a class as follows:

public class HttpFile : HttpPostedFileBase { // ... }

Where HttpFile can be created with a byte array like:

MyModel.File =3D new HttpFile(byteArray);

And MyModel.File is of type HttpPostedFileBase ...
Patrice replied on 11-Sep-09 04:34 AM
Sorry, my understanding is that you would like to expose some content that
were *previously* uploaded as an HttpPostedFileBase derived class ???

I really do not see the point. To me, by design this is something you will only
read from and it will be only there when the file is uploaded to the client.
I really do not catch why you would like to expose this file using the same
upload specific class during other requests...

Even to display the file you cannot anyway do it directly from a web page. So
it is likely you would have another page or handler that would get the byte
array from the entity given its id and stream the content so that it can be
referenced from another page using an img tag...

So for now I perfectly see whay you need to update your entity from the byte
array exposed by the uploaded file stream.

I do not see why you would like to expose to be able to construct a "posted
file" from some already posted content (this is not to display the file
??)...

--
Patrice


discussion :
46a9da24-f3aa-4202-9642-ceb65011af26@a21g2000yqc.googlegroups.com...
Patrice replied on 11-Sep-09 04:51 AM
This will not be used in the "real" application but this is perhaps for
testing ?

Have you tried my previous suggestion ? If it does not help, I would like to
understand the context in case I would need to show real code...

--
Patrice
shapper replied on 16-Sep-09 12:01 AM
t
nly
nt.

When uploading a file I get the following sequence:

1. View
The HTML page containing the form;
2. Controller
a) The binder gets the file into HttpPostedFileBase property;
b) Validate the file (For example: the ContentType or Size);
c) Map the ViewModel to Entity. HttpPostedFileBase.InputStream
goes to byte[];
if (ModelState.IsValid) {
articleRepository.Create(Mapper.Map<ArticleViewModel,
Article>(article));

When using a file I would have the following sequence:

1. Controller
a) Using the repository gets an Entity given its Id;
b) Map the Entity to ViewModel. byte[] should be used to fill
HttpPostedFileBase.

ArticleViewModel a =3D Mapper.Map<Article, ArticleViewModel>
(articleRepository.GetById(id))

I would also define the HttpPostedFileBase ContentType.
Then I send the ViewModel to the view, never the Entity.

2. View
...

I can use directly the byte[] from the Entity but I would like to have
this clear separation: Entity / ViewModel.
The moment the controller gets an Entity, maps it to ViewModel, and
never uses the Entity again.
Or the other way around ...

I have other options:
a) Have two properties on the ViewModel: byte[] and HttpPostedFileBase
b) Use the Entity byte[] directly on the controller
...

But I really would like to have this 1 : 1 Mapping.

So I would like to have a Property type in the ViewModel that:
1. Can be filled from the View (I think the options are
HttpPostedFileBase and HttpPostedFile but the first one is better for
testing)
2. Can be transformed to byte[]
3. Can be filled not only from the form but from a byte[]

Sorry, if I do not explain myself correctly.
Patrice replied on 11-Sep-09 10:53 AM
Ok, I still find thid design a bit strange. In particular I feel
uncomfortable about exposing HttpPostedFile, which is an Http based content
transfer mechanism as part of the View model...
To me the ViewModel should expose only the content (a byte array) that would
be written to by the controller from the postedfile...

Anyway If I had to follow this design, my first though would be likely to
use the class that inherits from HttpPostedFileBase mainly as a wrapper...

That is :

- by default the class uses an underlying HttpPostedFile to avoid having to
reimplement all this. All calls are forwarded to the HttpPostedFile...

- now If I am using a method that sets the content from user provided data
then I forget about the HTtpPostedFile and starts to forward the call to my
own memory stream based implementation

To avoid having to test in which case you are in each and every method you
could perhaps :
- create a IAdapter interface
- create a PostedFileAdapter class that will implement this interface
- create a MemoryStreamAdapter class that will implement this same interface

All these details are private implementation details anyway...

This way this class finally :
- instantiate a PostedFileAdapter (if possible)
- a particular method instantiate the MemoryStreamAdapter (a single method
with all data as for example the ContentType property is likely read only)
- the class just implements its method by forwarding the calls to the
current IAdapter whatever it is

You'll likely have also to handle the case where you have no posted files
and you never call the method that instantiate the base stream (you have no
Adapters or perhaps a NullAdapter).

Or you could perhaps just expose the file as an IContentAdapter and just
have concrete classes allowing to use a PostedFile, a MemoryStream or
whatever you want to provide the source for this content...

Does it  seems something you could start with  ?

--
Patrice