Reputation: 1
I use a image gallery plugin called Unite Gallery plugin in an ASP.NET MVC project in order to display images stored in database. However, loading all of the images at the same time takes too long time (because each photo is in 1MB-4MB size and loading 500 photos at the same time on page load is not a good idea) and I think there must be a better approach i.e. asenkron loading or partial loading. Here is my Razor and Controller code. I have a look at many pages on the wweb and docs, but there is not an example in the documentation page. Do you have any idea?
<div id="gallery" style="display:none;">
@foreach (var item in Model)
{
if (item.FileData != null)
{
var base64 = Convert.ToBase64String(item.FileData);
var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
<img alt='Image'
src="@imgSrc"
data-image="@imgSrc"
data-description='Image'>
}
}
</div>
<script type="text/javascript">
jQuery(document).ready(function () {
var gallery = jQuery("#gallery").unitegallery({
gallery_theme: "default" //theme skin
});
gallery.on("item_change", function (num, data) {
if((num%15) == 0)
{
$.ajax({
url: '@Url.Action("List", "PhotoContest")',
data: { isAll: isAllChecked, page: num }, //??? I pass the page parameter???
success: function(data){
//call is successfully completed and we got result in data
//??? NO IDEA ???
},
error:function (xhr, ajaxOptions, thrownError){
//some errror, some show err msg to user and log the error
alert(xhr.responseText);
}
});
}
});
});
</script>
public ActionResult List(string query)
{
var model = db.Photo.Select(m => new PhotoViewModel
{
Id = m.Id,
Name = m.Name,
StatusId = m.StatusId,
SubmitDate = m.SubmitDate,
FileAttachments = m.FileAttachments,
SubmitNo = m.SubmitNo
})
.ToArray();
return View("List", model);
}
Update:
After trying to apply @Kris's perfect approach, I encountered the error shown below. There is not a fix or solution regarding to this specific problem on the web. Any idea?
The image after page load overloads div and gallery borders as shown below:
Upvotes: 0
Views: 1330
Reputation: 1985
Load 30 images at a time
Load remaining in Itemchange event available in Unite gallery
Main Page
<div id="gallery" >
<input type="hidden" id="galleryPage" value="0"/>
@HTML.Action("GalleryImages") //first load 30 items as PageNo = 0
</div>
<script type="text/javascript">
var gallery;
jQuery(document).ready(function () {
gallery = jQuery("#gallery").unitegallery({
gallery_theme: "default" //theme skin
});
gallery.on("item_change", function (num, data) {
//when item loaded equals to 15 or 30 or multiples of 15 another 30 items get loaded
if((num%15) == 0)
{
$.ajax({
url: '@HTML.Action("GalleryImages")'+"?pageNo="+jQuery("galleryPage").val(),
data: { isAll: isAllChecked },
success: function(data){
jQuery("gallery").append(data);//partial view with new images
jQuery("galleryPage").val(gallery.getNumItems()/30); //page number total items/number of items per page
},
error:function (xhr, ajaxOptions, thrownError){
//some errror, some show err msg to user and log the error
alert(xhr.responseText);
}
});
}
});
});
</script>
Partial View (_galleryImages.cshtml)
@foreach (var item in Model)
{
if (item.FileData != null)
{
var base64 = Convert.ToBase64String(item.FileData);
var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
<img alt='Image'
src="@imgSrc"
data-image="@imgSrc"
data-description='Image'>
}
}
Controller
//Main View
public ActionResult List()
{
return View();
}
//Partial View
public Action GalleryImages(int PageNo)
{
int PageSize = 30;
var model = db.Photo.Select(m => new PhotoViewModel
{
Id = m.Id,
Name = m.Name,
StatusId = m.StatusId,
SubmitDate = m.SubmitDate,
FileAttachments = m.FileAttachments,
SubmitNo = m.SubmitNo
}).Skip(PageNo*PageSize).Take(PageSize).ToArray();
return PartialView("_galleryImages", model);
}
Upvotes: 2
Reputation: 239400
I don't think there's just one issue here. First, loading 100s of images all at once is going to be slow no matter what you do. For this point @Kris probably has the right idea. I'm unfamiliar with this particular library, but if it provides a way to progressively load in a handful of images at a time, you should definitely make use of that.
The second issue is that you're using base64-encoded data URIs. Images encoded in this way are roughly 150% as large as the actual image data itself. In other words, you're adding greater stress to an already stressed situation. Instead, you should have an action that returns the image data, something like:
public ActionResult GetImage(int id)
{
var image = db.Images.Find(id);
if (image == null)
{
return new HttpNotFoundResult();
}
return File(image.FileData, image.FileType);
}
You can get somewhat creative here by caching the database query result or even the entire response, but be advised that you'll need a significant amount of RAM, since you're going to be storing a lot of image data there.
Third, there's the issue of using a database to store image data in the first place. Just because databases provide a blob type, doesn't mean you need to use it. The most performant approach is always going to be serving directly from the filesystem, as IIS can serve static files directly, without involving all the ASP.NET machinery. Instead of storing the image data in your database, write the image to a filesystem location and then merely store the path to the image in the database. You could then optimize even further by actually offloading all the images to a CDN, ensuring super-fast delivery and taking virtually all load off your server.
Upvotes: 1