Reputation: 12874
I have an MVC app and I want on the top of the master page to have a series of random images from the folder.
To do this I have to write code but not where to write the code?? It has to be done in one place.
I will probably just set the images at the beginning of the session so they are cached by the browser and improve performance of the site.
Malcolm
Upvotes: 2
Views: 3071
Reputation: 1010
Caching is Key
As the others have said, you've got to use caching since you are performing repetitive disk I/O on data that doesn't change often.
My example creates and caches a List<T>
of the image file paths you will need for each subsequent request. System.Web.Caching
is perfect for this because you can create a CacheDependency
object directly on your image directory -- if a file gets changed or added, your cache is automatically invalidated. It is then recreated the next time it is requested.
Avoiding Duplicates with the HashSet<T>
I bet you don't want two of the same pictures ever showing up in your header!
Randomizing using Random.Next
does not exclude previously generated duplicates. I used a HashSet<T>
as a poor man's unique randomizer since the HashSet<T>
will only allow you to add unique values.
The Model
This operation should be part of your model in MVC. You change it to go along with your other data fetching classes as you see fit.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Caching;
public class RandomImage
{
public static string[] GetImages(string folder, int count)
{
HttpContext context = HttpContext.Current;
string virtualFolderPath = string.Format("/content/{0}/", folder);
string absoluteFolderPath = context.Server.MapPath(virtualFolderPath);
Cache cache = context.Cache;
var images = cache[folder + "_images"] as List<string>;
// cache string array if it does not exist
if (images == null)
{
var di = new DirectoryInfo(absoluteFolderPath);
images = (from fi in di.GetFiles()
where fi.Extension.ToLower() == ".jpg" || fi.Extension.ToLower() == ".gif"
select string.Format("{0}{1}", virtualFolderPath, fi.Name))
.ToList();
// create cach dependency on image randomFolderName
cache.Insert(folder + "_images", images, new CacheDependency(absoluteFolderPath));
}
Random random = new Random();
var imageSet = new HashSet<string>();
if (count > images.Count())
{
throw new ArgumentOutOfRangeException("count");
}
while (imageSet.Count() < count)
{
//using an hashset will ensure a random set with unique values.
imageSet.Add(images[random.Next(count)]);
}
return imageSet.ToArray();
}
}
The Controller
Access the method in your controller something like....
string[] images = Models.RandomImage.GetImages("myPictures", 4);
Upvotes: 3
Reputation: 532615
I'd want to make sure that simply reading the directory each time and generating the file names is a real bottleneck before I'd do anything more complicated. It's certainly less efficient, but the code is arguably simpler. If it isn't a bottleneck, then the controller action responsible for rendering the view is the right place for the code. Note that you'll need to turn of output caching for the action (if it isn't already) if you want the image to change each time.
If it turns out that reading the file names and constructing the links really is a bottleneck -- it may take much less time than reading the actual file -- AND the files in the directory can change while the application is running, then constructing a collection of them and storing them in the session on login is a reasonable way to handle it. The caveat is that if the application is also responsible for uploading the images (so that it knows when they change), you might be able to load them at application start and keep them in a global instance which is updated by the application as images are uploaded (or removed).
Once you have your collection of images (by reading each time, from the session, or a global instance), use a random number generator to pick the image that you want to display or a set that you want to rotate through using something like the jQuery Cycle plugin, pass it (them) to the view and have it render the image tags.
Upvotes: 0
Reputation: 20203
Well, to get the images:
string[] get_images(string folder) {
string[] files = Directory.GetFiles(folder, "*.jpg"/* or whatever */);
List<string> rand = new List<string>();
Random r = new Random();
for ( int i = 0; i < numImages; i++ ) {
rand.Add(Path.GetFileName(files[r.Next(files.Length-1)]));
}
return rand.ToArray();
}
And then in the master page:
<% PrintImages(); %>
Where PrintImages() is:
string[] img = get_images(Server.MapPath("~/Content/RandomImages"));
foreach (string i in img) { Response.Write("<img src=\"/Content/RandomImages/"+i+"\" />"); }
That is a rough solution, and caching would be good - that would really thrash a disk.
Upvotes: 2
Reputation: 115859
Write a helper function that would get a random filename from an array of filenames which is generated at application startup.
Upvotes: 2