HttpPostedFileBase and byte[]

Discussion in 'Microsoft C# .NET' started by shapper, Sep 10, 2009.

  1. shapper

    shapper Guest

    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, Sep 10, 2009
    #1
    1. Advertisements

  2. shapper

    shapper Guest

    I can't 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
     
    shapper, Sep 10, 2009
    #2
    1. Advertisements

  3. shapper

    Patrice Guest

    Do you need a file stream ? You could perhaps use a stream that doesn't save
    to disk such as a MemoryStream. Allso IMO the stream shouldn't 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'm 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

    "shapper" <> a écrit dans le message de groupe de
    discussion :
    ...
    > I can't 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, Sep 10, 2009
    #3
  4. * Patrice wrote, On 10-9-2009 18:29:
    > Do you need a file stream ? You could perhaps use a stream that doesn't save
    > to disk such as a MemoryStream. Allso IMO the stream shouldn't be closed or
    > at the end so that a consumer could read the file content...


    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 aren't too big and if you don't
    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're 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're
    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

    > 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'm 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...



    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);
    }
    else
    {
    _Stream = new MemoryStream();
    }
    }
    return _Stream;
    }
    }
    private Stream _Stream;

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

    public HttpFile(String fileName, String contentType, byte[]
    contentData) {
    this._ContentType = contentType;
    this._FileName = fileName;
    this._Stream = new MemoryStream(contentData);
    } // HttpFile


    public void Dispose()
    {
    Dispose(true);
    GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
    // if disposing is true, Dispose was called. Need to clean
    up Managed objects. This isn't needed when
    // We're being called from a Finalizer method.
    if (disposing)
    {
    if (_Stream != null)
    {
    try{
    _Stream.Dispose();
    }catch{}
    }
    }

    // After disposing Managed objects also unmanaged objects
    should be disposed.
    }

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

    }


    --
    Jesse Houwing
    jesse.houwing at sogeti.nl
     
    Jesse Houwing, Sep 10, 2009
    #4
  5. shapper

    shapper Guest

    > IMO you should start by explaining what is the overall goal.

    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, Sep 10, 2009
    #5
  6. shapper

    shapper Guest

    On Sep 10, 7:02 pm, shapper <> wrote:
    > > IMO you should start by explaining what is the overall goal.

    >
    > 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...
    >
    > 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 ...


    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 = new HttpFile(byteArray);

    And MyModel.File is of type HttpPostedFileBase ...
     
    shapper, Sep 10, 2009
    #6
  7. shapper

    Patrice Guest

    Sorry, my understanding is that you would like to expose some content that
    were *previously* uploaded as an HttpPostedFileBase derived class ???

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

    Even to display the file you can't anyway do it directly from a web page. So
    it's 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 don't 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


    "shapper" <> a écrit dans le message de groupe de
    discussion :
    ...
    >> IMO you should start by explaining what is the overall goal.

    >
    > 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 ...
    >
    >
    >
    >
     
    Patrice, Sep 11, 2009
    #7
  8. shapper

    Patrice Guest

    > I don't 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
    > ??)...


    This will not be used in the "real" application but this is perhaps for
    testing ?

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

    --
    Patrice
     
    Patrice, Sep 11, 2009
    #8
  9. shapper

    shapper Guest

    On Sep 11, 9:34 am, "Patrice" <http://scribe-en.blogspot.com/> wrote:
    > Sorry, my understanding is that you would like to expose some content that
    > were *previously* uploaded as an HttpPostedFileBase derived class ???
    >
    > I really don't see the point. To me, by design this is something you'll only
    > read from and it will be only there when the file is uploaded to the client.


    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 = 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 don't explain myself correctly.
     
    shapper, Sep 11, 2009
    #9
  10. shapper

    Patrice Guest

    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'm 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
     
    Patrice, Sep 11, 2009
    #10
    1. Advertisements

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric Hendriks

    How to marshal a managed byte[][] to unmanaged byte **???

    Eric Hendriks, Aug 18, 2003, in forum: Microsoft C# .NET
    Replies:
    1
    Views:
    402
    Nicholas Paldino [.NET/C# MVP]
    Aug 18, 2003
  2. Tim Conner

    Moving from byte[] to byte[]

    Tim Conner, Oct 10, 2003, in forum: Microsoft C# .NET
    Replies:
    2
    Views:
    136
    Dario
    Oct 10, 2003
  3. Eka Gautama

    Convert Bitmap to Byte and Byte to Bitmap

    Eka Gautama, Nov 10, 2003, in forum: Microsoft C# .NET
    Replies:
    2
    Views:
    480
    Eka Gautama
    Nov 11, 2003
  4. David Vestal

    Passing a C# byte[] as a C++ System::Byte*

    David Vestal, Jan 15, 2004, in forum: Microsoft C# .NET
    Replies:
    1
    Views:
    424
    Mattias Sjögren
    Jan 15, 2004
  5. Replies:
    1
    Views:
    191
    Jon Skeet [C# MVP]
    Dec 17, 2006
Loading...

Share This Page