Displaying database image (bytes[]) in Razor/MVC3?

2019-01-22 03:33发布

问题:

I am returning a dataset from my MS SQL 2008R2 database that contains a datatable that I am already getting data from on my Razor view. I added a byte[] field that contains an image thumbnail that I am trying to display on that view.

Since the byte array is relatively tiny, I figured I would try displaying the byte array inline. However, after reading there may be some browser-related issues, I abandoned this.

Creating a controller method seemed the way to go, (both methods found here), however I already have the byte array ready to go on my view, and don't need to make another db call to get it based on an ID.

Doing this, doesn't work for obvious reasons:

... displaying other data on the page ....

@if (Model.dsResults.Tables[0].Rows[i]["ImageBytes"] == null)
{
    <img src="@Url.Content("~/Content/Images/NoPhoto.png")" border="0" />
}
else
{
    <img src="@Url.Action("GetImage", "SearchResults", new { imageBytes = Model.dsResults.Tables[0].Rows[i]["ImageBytes"] })" alt="Product Image" />
}

...

Controller method:

[Authorize]
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetImage(byte[] imageBytes)
{
    byte[] byteArray = imageBytes;
    return new FileContentResult(byteArray, "image/jpeg");
}

... as this essentially is attempting to send the byte array over http.

So, my question is, since I already have the byte array on my Razor view, how do I display it?

-- Update --

I realize that doing processing in the view isn't recommended, but since the data is already there, can't something like this be done:

Response.Write((byte[])Model.dsResults.Tables[0].Rows[i]["ImageBytes"]);

Or...

Stream s = new MemoryStream(((byte[])Model.dsResults.Tables[0].Rows[i]["ImageBytes"]));
System.Drawing.Image img = new System.Drawing.Bitmap(s);

In other postings I've read that you an do a Response.Write(byte[]...)) but this doesn't work in this case.

-- UPADATE --

In my ongoing search for efficiency (not having to make another request to the db) the WebImage helper seems to be a good candidate. One of its constructors will accept a byte array to initialize the class, then using the .Write("jpeg") method, I can see the image.

<td>
    @{
        WebImage webImage = new WebImage(((byte[])Model.dsResults.Tables[0].Rows[i]["ImageBytes"]));
        webImage.Write("jpeg");
    }
</td>

The problem with using WebImage.Write() is that once used, the image is the only thing that renders on the page. Is there a way to render this directly to a "control" on a Razor view page?

-- UPDATE --

This continues to bug me... so I tried the following, which I figured may work, since this is what we're doing from the Action...

if (Model.dsResults.Tables[0].Rows[i]["ImageBytes"] != DBNull.Value)
{
    var img1 = new FileContentResult(((byte[])Model.dsResults.Tables[0].Rows[i]["ImageBytes"]), "image/jpeg");
    <text>
        <img src="@new FileContentResult(((byte[])Model.dsResults.Tables[0].Rows[i]["ImageBytes"]), "image/jpeg")" />
    </text>
}

... doesn't work.

回答1:

Model.Content is a byte[] image.

@{
    string imageBase64 = Convert.ToBase64String(Model.Content);
    string imageSrc = string.Format("data:image/gif;base64,{0}", imageBase64);
}

Used like so:

<img src="@imageSrc" alt="@Model.Name" width="100" height="100" />


回答2:

Eventually, I went for the two trips route. Highly inefficient, as the image bytes were already ready to go on the view. There should be a web helper image control that will render the image directly from the image's byte array (or image type). Maybe there is and I missed it.

To get the bytes (yes, all over again):

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult GetThumbnailImage(string itemListID)
    {
        byte[] imageBytes = null;

        client = new FeederServiceClient();
        imageBytes = client.GetItemThumbnail( itemListID );

        if (imageBytes == null)
        {
            return new FilePathResult("~/Content/Images/NoPhoto.png", "image/png");
        }
        else
        {
            return new FileContentResult(imageBytes, "image/jpeg");
        }
    }

Then to display the image:

<img src="@Url.Content("~/Thumbnails/" + @Model.dsResults.Tables[0].Rows[i]["ItemListID"] )" />

With the following route placed in Global.asax:

routes.MapRoute(name: "Thumbnails", url: "Thumbnails/{itemListID}", defaults: new { controller = "Results", action = "GetThumbnailImage" });


回答3:

I don't think having a byte array in your View will help you. You'll need to use a controller action as the img src as described in your linked answer.

[Authorize]
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Image(int id)
{
    byte[] byteArray = _imageRepository.GetImage(id);
    return new FileContentResult(byteArray, "image/jpeg");
}


<img src="Image/1234" alt="Image 1234"/>


回答4:

The following will work:

<img src="data:image/jpg;base64,@(Convert.ToBase64String(item.PhotoImage))" alt="@item.orientation" />

in my application, I defined the model on the RAZOR page as item, rather than model. The PhotoImage is my byte array (actually image field in SQL) and Orientation is just a string column that tells metadata about the photo.

I searched for a long time before I found this answer and it worked for me. Hope it helps.



回答5:

You can just do a Response.Write(your byte array) and set the Response.ContentType to image/jpeg in your Controller action. That will display it directly as an image.

UPDATE

The code could look something like this:

    [Authorize]
    [AcceptVerbs(HttpVerbs.Get)]
    public void GetImage(int id)
    {
        byte[] imageArray = GetImageBytesFromDb(id); // some method for returning the byte-array from db.
        Response.ContentType = "image/png";
        Response.Write(imageArray);       
    }

Now you would be able to do the following in your view:

 <img src="@Url.Action("GetImage", "SearchResults", new { id= Model.dsResults.Tables[0].Rows[i]["Id (or whatever the id of the row is)"] })" alt="Product Image" />

So, in other words instead of using the bytes directly in the view, you call a controller-action that returns the bytes from the database-row based on the Id of the row (or product, if that's the entity of the row)

It might not be exactly an answer to your question, but it will work.



回答6:

This is one way to avoid the second unnecessary trip to the database. memorystream to Image but it comes with it's own problem when used in IE9 (all other browsers and IE versions seem to work OK).

Even more information about the problem can be found here More problem details.

In the end, for the sake of not having a hack for what I think is probably a bug in IE9, I went with the 'two' reads, but there shouldn't be a need.



回答7:

If you don't mind using Session, you can put your model into Session (heresy!), then render your view as normal but now where you want the images to appear you call the controller action with Url.Action passing it the Id of the image you want. Then pull your model back out of session, find the image in your model for that id, do the FiLeContentResult(array,imgtype) on your byte array and viola - images direct from your model onto your page. No need to go to the real database again BUT if you use Session SQL State then obv. you will be hitting the database much worse than before but if you are InProc.... well it's one way to do it. I'm not sure if IE9 still has a minor problem with caching, but you can always pass a unique id along with your real id to make sure that doesn't happen. It's a shabby solution but it does work.