I'm trying to create a simple image gallery. Uploading works, but I have problems with displaying those images.
In the internet I found that this is the right way to do it:
[NotMapped]
public string ImageUrl
{
get
{
var isEmpty = String.IsNullOrWhiteSpace(ImgName);
if(isEmpty)
{
return Path.Combine("~/Images", "noImageAvailable.png");
}else
{
return Path.Combine("~/Images/uploaded", ImgName);
}
}
}
and in the view
#foreach (var item in Model) {
...
#{ var imgPath = HostingEnvironment.MapPath(item.ImageUrl); }
<img src="#Url.Content(imgPath)"/>
Which produces:
<img src="C:\......\Images\uploaded\0_634927659072110613.jpg"/>
What worked for me is:
[NotMapped]
public string ImageUrl
{
get
{
var isEmpty = String.IsNullOrWhiteSpace(ImgName);
if(isEmpty)
{
return Path.Combine("../Images", "noImageAvailable.png");
}else
{
return Path.Combine("../Images/uploaded", ImgName);
}
}
}
and in the view:
#foreach (var item in Model) {
.....
<img src="#Url.Content(item.ImageUrl)"/>
Which produces:
<img src="../Images/uploaded\0_634927649098750170.jpg"/>
Remarks
There are 2 differences:
~/Images vs ../Images
the lines in the view
What is interesting is that the line
<img src="C:\......\Images\uploaded\0_634927659072110613.jpg"/>
displays an image in a static standalone file, while when it's served by the IIS it doesn't. There is just blank space instead of an image.
My question is what is the right approach, and also why does't the first one work?
Your code is doing exactly what you're asking. The 1st one doesn't work because var imgPath = HostingEnvironment.MapPath(item.ImageUrl); returns a physical path on the server to the image URL, NOT a URL. Use the 1st block with <img src="#Url.Content(item.ImageUrl)"/>. This looks like:
[NotMapped]
public string ImageUrl
{
get
{
var isEmpty = String.IsNullOrWhiteSpace(ImgName);
if(isEmpty)
{
return Path.Combine("~/Images", "noImageAvailable.png");
}else
{
return Path.Combine("~/Images/uploaded", ImgName);
}
}
}
with your view:
#foreach (var item in Model) {
.....
<img src="#Url.Content(item.ImageUrl)"/>
"~/Images/..." in .net says from web root, find the folder Images,
then ...
"../Images/..." is relative to the current location - go up
one folder, find folder images, then ...
Related
I currently have the following code:
[HttpPost]
public ActionResult Index(IList<LocalPageModel> postPages,
IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
foreach (HttpPostedFileBase file in files)
{
if ((file != null) && (file.ContentLength > 0))
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/"),
fileName);
file.SaveAs(path);
}
}
}
else
{
ManagePagesModel mod = new ManagePagesModel
{
PostPages = postPages
};
return View("Index", mod);
}
return RedirectToAction("Index");
}
In my view, I have a JavaScript button which will add a div so that the user can post another page such as:
$("#add-page").click(function () {
$("#page").append('<div id="page"> #Html.TextBoxFor(u => u.PostPages[0].Title) </div>');
});
How do I make it so that when the user clicks on the JavaScript button, the new text will be appended to the page and u.PostPages[x] will be incremented?
If you want to do it all on the client (no AJAX), maybe don't use the MVC helpers at all, and do it manually instead - you know the HTML that will be rendered, so just do that:
var i = 0;
$("#add-page").click(function () {
$("#page").append('<input type="text" name="PostPages[' + (i++) + '].Title">');
});
Maybe clean the code up a bit so the quotes don't get too confusing, but you get the idea...
You didn't past your view, but I assume you have the following at the top:
#model = ManagePagesModel
If that's the case, you can then use the following #foreach to loop through the page models:
$("#add-page).click(function() {
#foreach(var pageModel in Model.PostPages){
$("#page").append('<div id="page"> #Html.TextBoxFor(u => pageModel.Title) </div>');
});
To increment u.PostPages[x] you may use following code:
<script>
var i = 0;
$("#add-page").click(function () {
i++
$("#page").append('<div id="page"> #Html.TextBoxFor(u => u.PostPages['+i+'].Title') </div>');
});
</script>
Here is small working example: jsfiddle
I am new to MVC.I have saved the image and some data but can't display the saved images. I want to display all saved images in the main page.
Model: Get the db list from model
public List<Products> GenreList()
{
}
Controller
public ActionResult MYsample()
{
var MyList = storeDB.GenreList();
var a= MyList.Count;
if (a != null)
{
foreach (var li in MyList)
{
return File(li.fileContent, li.mimeType,li.fileName);
}
}
return View(MyList);
}
View
#foreach (var item in Model)
{
<img src="#Url.Action("MYsample", "HomeController", new { id = item.ProductID })" alt="#item.Productname" />
}
You could start by writing a controller action that will serve the images to the response stream:
public ActionResult Image(int id)
{
// you should add a method to your repository that returns the image
// record from the id
Products image = storeDB.Get(id);
return File(image.fileContent, image.mimeType);
}
and then in your main controller action send the list of images to the view:
public ActionResult MySample()
{
var model = storeDB.GenreList();
return View(model);
}
and then in your strongly typed view loop through the images and generate an <img> tag for each image by pointing its src property to the newly created controller action:
#model MyList
#foreach (var li in MyList)
{
<img src="#Url.Action("Image", new { id = li.Id })" alt="" />
}
If you don't want to have a separate controller action that will query the database and retrieve the image record from an id you could use the data URI scheme. Bear in mind though that this is not supported by all browsers.
So the idea is that your controller action will send the image data to the view:
public ActionResult MySample()
{
var model = storeDB.GenreList();
return View(model);
}
and then inside your strongly typed view you could loop through the list and generate the proper <img> tag:
#model MyList
#foreach (var li in MyList)
{
<img src="src="data:#(li.mimeType);base64,#(Html.Raw(Convert.ToBase64String(li.fileContent)))" alt="" />
}
Basically I want to implement simple search functionality, whenever user enters some keyword in the text box on view and clicks submit button I want to make ASYNC calls to predefined website urls using TPL Async mechanism. When I do the same with console application it works like a charm but not with ASP.NET MVC3.
I couldn't find the reason
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
[HttpPost]
public ActionResult Index(string text)
{
string[] url = { "http://www.msnbc.com", "http://www.yahoo.com",
"http://www.nytimes.com", "http://www.washingtonpost.com",
"http://www.latimes.com", "http://www.newsday.com" };
Task<string[]> webTask = this.GetWordCounts(url, text);
string[] results = null;
try
{
results = webTask.Result;
}
catch (AggregateException e)
{
}
return View("Index", results);
}
//Taken from MSDN
Task<string[]> GetWordCounts(string[] urls, string name)
{
TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
WebClient[] webClients = new WebClient[urls.Length];
object m_lock = new object();
int count = 0;
List<string> results = new List<string>();
for (int i = 0; i < urls.Length; i++)
{
webClients[i] = new WebClient();
#region callback
// Specify the callback for the DownloadStringCompleted
// event that will be raised by this WebClient instance.
webClients[i].DownloadStringCompleted += (obj, args) =>
{
if (args.Cancelled == true)
{
tcs.TrySetCanceled();
return;
}
else if (args.Error != null)
{
// Pass through to the underlying Task
// any exceptions thrown by the WebClient
// during the asynchronous operation.
tcs.TrySetException(args.Error);
return;
}
else
{
// Split the string into an array of words,
// then count the number of elements that match
// the search term.
string[] words = null;
words = args.Result.Split(' ');
string NAME = name.ToUpper();
int nameCount = (from word in words.AsParallel()
where word.ToUpper().Contains(NAME)
select word)
.Count();
// Associate the results with the url, and add new string to the array that
// the underlying Task object will return in its Result property.
results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, name));
}
// If this is the last async operation to complete,
// then set the Result property on the underlying Task.
lock (m_lock)
{
count++;
if (count == urls.Length)
{
tcs.TrySetResult(results.ToArray());
}
}
};
#endregion
// Call DownloadStringAsync for each URL.
Uri address = null;
try
{
address = new Uri(urls[i]);
// Pass the address, and also use it for the userToken
// to identify the page when the delegate is invoked.
webClients[i].DownloadStringAsync(address, address);
}
catch (UriFormatException ex)
{
// Abandon the entire operation if one url is malformed.
// Other actions are possible here.
tcs.TrySetException(ex);
return tcs.Task;
}
}
// Return the underlying Task. The client code
// waits on the Result property, and handles exceptions
// in the try-catch block there.
return tcs.Task;
}
this is my view - for now I have hard coded keyword as microsoft
#using (Html.BeginForm("Index", "Home", new { text = "Microsoft" }))
{
<input type="submit" />
}
Update: It stays forever and inside the try block of Index Post method
I would recommend you using an AsyncController for this task to avoid jeopardizing ASP.NET worker threads which is one the worst thing that might happen to an ASP.NET application => running out of worker threads. It's like running out of fuel in the middle of the desert. You most certainly die.
So let's start by writing an extension method that will allow us converting the legacy WebClient event based pattern into the new task based pattern:
public static class TaskExtensions
{
public static Task<string> DownloadStringAsTask(this string url)
{
var tcs = new TaskCompletionSource<string>(url);
var client = new WebClient();
client.DownloadStringCompleted += (sender, args) =>
{
if (args.Error != null)
{
tcs.SetException(args.Error);
}
else
{
tcs.SetResult(args.Result);
}
};
client.DownloadStringAsync(new Uri(url));
return tcs.Task;
}
}
Armed with this extension method in hand we could now define a view model that will basically reflect the requirements of our view:
public class DownloadResultViewModel
{
public string Url { get; set; }
public int WordCount { get; set; }
public string Error { get; set; }
}
Then we move on to an asyncrhonous controller that will contain 2 actions: a standard synchronous Index action that will render the search form and an asynchronous Search action that will perform the actual work:
public class HomeController : AsyncController
{
public ActionResult Index()
{
return View();
}
[AsyncTimeout(600000)]
[HttpPost]
public void SearchAsync(string searchText)
{
AsyncManager.Parameters["searchText"] = searchText;
string[] urls =
{
"http://www.msnbc.com",
"http://www.yahoo.com",
"http://www.nytimes.com",
"http://www.washingtonpost.com",
"http://www.latimes.com",
"http://www.unexistentdomainthatwillcrash.com",
"http://www.newsday.com"
};
var tasks = urls.Select(url => url.DownloadStringAsTask());
AsyncManager.OutstandingOperations.Increment(urls.Length);
Task.Factory.ContinueWhenAll(tasks.ToArray(), allTasks =>
{
var results =
from task in allTasks
let error = task.IsFaulted ? task.Exception.Message : null
let result = !task.IsFaulted ? task.Result : string.Empty
select new DownloadResultViewModel
{
Url = (string)task.AsyncState,
Error = error,
WordCount = result.Split(' ')
.Where(x => string.Equals(x, searchText, StringComparison.OrdinalIgnoreCase))
.Count()
};
AsyncManager.Parameters["results"] = results;
AsyncManager.OutstandingOperations.Decrement(urls.Length);
});
}
public ActionResult SearchCompleted(IEnumerable<DownloadResultViewModel> results)
{
return View("index", results);
}
}
Now we define an ~/Views/Home/Index.cshtml view that will contain the search logic as well as the results:
#model IEnumerable<DownloadResultViewModel>
#using (Html.BeginForm("search", null, new { searchText = "politics" }))
{
<button type="submit">Search</button>
}
#if (Model != null)
{
<h3>Search results</h3>
<table>
<thead>
<tr>
<th>Url</th>
<th>Word count</th>
</tr>
</thead>
<tbody>
#Html.DisplayForModel()
</tbody>
</table>
}
And of course the corresponding display template that will be rendered automatically for each element of our model (~/Views/Shared/DisplayTemplates/DownloadResultViewModel.cshtml):
#model DownloadResultViewModel
<tr>
<td>#Html.DisplayFor(x => x.Url)</td>
<td>
#if (Model.Error != null)
{
#Html.DisplayFor(x => x.Error)
}
else
{
#Html.DisplayFor(x => x.WordCount)
}
</td>
</tr>
Now, since the search operation could take quite a long time your users could quickly get bored without being able to use some of the other hundredths of functionalities that your webpage has to offer them.
In this case it is absolutely trivial to invoke the Search controller action using an AJAX request and showing a spinner to inform the users that their search is in progress but without freezing the webpage allowing them to do other things (without navigating away from the page obviously).
So let's do that, shall we?
We start by externalizing the results into a partial (~/Views/Home/_Results.cshtml) without touching at the display template:
#model IEnumerable<DownloadResultViewModel>
#if (Model != null)
{
<h3>Search results</h3>
<table>
<thead>
<tr>
<th>Url</th>
<th>Word count</th>
</tr>
</thead>
<tbody>
#Html.DisplayForModel()
</tbody>
</table>
}
and we adapt our ~/Views/Home/Index.cshtml view to use this partial:
#model IEnumerable<DownloadResultViewModel>
#using (Html.BeginForm("search", null, new { searchText = "politics" }))
{
<button type="submit">Search</button>
}
<div id="results">
#Html.Partial("_Results")
</div>
and of course the SearchCompleted controller action that must now return only the partial result:
public ActionResult SearchCompleted(IEnumerable<DownloadResultViewModel> results)
{
return PartialView("_Results", results);
}
Now all that's left is to write a simple javascript that will AJAXify our search form. So this could happen into a separate js that will reference in our layout:
$(function () {
$('form').submit(function () {
$.ajax({
url: this.action,
type: this.method,
success: function (results) {
$('#results').html(results);
}
});
return false;
});
});
Depending on whether you referenced this script in the <head> section or at the end of the body you might not need to wrap it in a document.ready. If the script is at the end you could remove the wrapping document.ready function from my example.
And the last part is to give some visual indication to the user that the site is actually performing a search. This could be done using a global ajax event handler that we might subscribe to:
$(function () {
$(document).ajaxStart(function () {
$('#results').html('searching ...');
});
});
I may be doing it wrong... so please correct me if i am.
I've pulled my Last.FM data from their RestAPI and cached it (to be refreshed only if the cache is greater than 30 minutes old), and from there i've loaded it into an enumerated list of tracks.
I'm attempting to drop that logic in the Razor display, and never managed to make it work with more than just the foreach and if (item.image...)..., adding in the logic to drop the divs has caused razor to loose track of the closing bracket for the foreach.
Am I making this too complicated?
<!-- lfm data -->
#* Iterate over the Last.FM data and display it in an attractive format *#
#foreach (var group in Model.Select((x, i) => new { Group = i / 4, Item = x })
.GroupBy(x => x.Group)) {
<div class="LFM-Data">
foreach(var x in group) {
if (x.item.image != null) {
<img src="#x.item.image.ToString()" class="lfm-artwork" alt="#x.item.album"/>
} else {
<img src="/Content/images/lfm/NoAlbumArt.jpg" class="lfm-artwork" alt="No Album Art Available" />
}
<p>#Html.Raw(x.item.name.ToString())</p>
}
</div>
}
after following Equiso's suggestion, i'm getting an odd scoping issue where either X is not in the current scope, or x does not contain a property for image...
#model IEnumerable<CCBlog.Classes.LastFmWrapper.Track>
and this is part of the LFM Wrapper class --- that i'm modeling the data after
public struct Track
{
public string artist { get; set; }
public string name { get; set; }
public string album { get; set; }
public string image { get; set; }
}
I call shenanigans!
If you are trying to display the items in groups of 4 you could do somethig like this
#foreach (var group in Model.Select((x, i) => new { Group = i / 4, Item = x })
.GroupBy(x => x.Group)) {
<div class="LFM-Data">
foreach(var x in group) {
if (x.Item.image != null) {
<img src="#x.Item.image.ToString()" class="lfm-artwork" alt="#x.Item.album"/>
} else {
<img src="/Content/images/lfm/NoAlbumArt.jpg" class="lfm-artwork" alt="No Album Art Available" />
}
<p>#Html.Raw(x.Item.name.ToString())</p>
}
</div>
}
or you could create a ViewModel that get the items grouped already.
I have a view which contains a users id and an image column.
Here's what i've tried doing to retrieve the image but i keep getting a box with an red x instead of the actual image.
View
<td><img src="<%= Url.Action( "DisplayImage" , "User" , new { id = item.id} ) %>" alt="" /></td>
Controller
public FileContentResult DisplayImage(string id)
{
byte[] image = repository.GetImage(id);
return File(image, "image/jpg");
}
i've also tried returning an ActionResult instead and that didn't work either.
Repository
public Byte[] GetImage(string id)
{
var image = db.GetImage(id).First<GetImageResult>();
if (image == null)
return null;
return image.UserImage;
}
LinqTOSQL Class
[Function(Name="dbo.GetImage")]
public ISingleResult<GetImageResult> GetImage([Parameter(DbType="VarChar(8)")] string id)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), id);
return ((ISingleResult<GetImageResult>)(result.ReturnValue));
}
public partial class GetImageResult
{
private System.Byte[] _userImage;
public GetImageResult()
{
}
[Column(Storage="_userImage", DbType="Image")]
public System.Byte[] UserImage
{
get
{
return this._userImage;
}
set
{
if ((this. _userImage!= value))
{
this. _userImage = value;
}
}
}
}
I've been killing myself all day trying to get this to work, but it just isn't working.
The return type on the stored procedure is an integer (atleast when i look at parameters in
SQL Server Management Studio it says integer), but i can't redefine that now can i?
It's actually hitting the DisplayImage Action with the correct parameters within the UserController and returning File(imageByteArray, "image/jpg") but only a box with red x is being displayed.
Any help would be greatly appreciated.
edit: I've tried debugging by adding a Reponse.BinaryWrite(imageByteArray) within the action result and hitting the url directly by goign to http://localhost/User/DisplayImage?id=10101010 and the image for that user is displayed in mspaint.
edit2: I also did a view source and my html for that image tag came out as following.
<td>
<img src='/User.mvc/GetImage?id=U00915441' alt="" />
</td>
Thanks
Look at this question I had from a while back - the solution was special ActionResult type for images
Edit: Here's my code. I'm actually creating an ImageResult class from an Image that I created with GDI+ like this :
return new ImageResult()
{
ImageFormat = spriteInfo.ImageFormat,
EncodedImageBytes = spriteInfo.GetImageStream()
};
The image result class is. You'll notice if I provide an EncodedImageBytes parameter it will send that to the output stream. This looks like exactly what you want. On the other hand if you're just passing in an Image then it will just write that Image out to the output stream.
public class ImageResult : ActionResult
{
public ImageResult() { }
public int? Quality { get; set; }
public Image Image { get; set; }
public ImageFormat ImageFormat { get; set; }
public byte[] EncodedImageBytes { get; set; }
public override void ExecuteResult(ControllerContext context)
{
// verify properties
if (EncodedImageBytes == null)
{
if (Image == null)
{
throw new ArgumentNullException("Image");
}
}
if (ImageFormat == null)
{
throw new ArgumentNullException("ImageFormat");
}
// output
context.HttpContext.Response.Clear();
if (ImageFormat.Equals(ImageFormat.Bmp)) context.HttpContext.Response.ContentType = "image/bmp";
if (ImageFormat.Equals(ImageFormat.Gif)) context.HttpContext.Response.ContentType = "image/gif";
if (ImageFormat.Equals(ImageFormat.Icon)) context.HttpContext.Response.ContentType = "image/vnd.microsoft.icon";
if (ImageFormat.Equals(ImageFormat.Jpeg)) context.HttpContext.Response.ContentType = "image/jpeg";
if (ImageFormat.Equals(ImageFormat.Png)) context.HttpContext.Response.ContentType = "image/png";
if (ImageFormat.Equals(ImageFormat.Tiff)) context.HttpContext.Response.ContentType = "image/tiff";
if (ImageFormat.Equals(ImageFormat.Wmf)) context.HttpContext.Response.ContentType = "image/wmf";
// output stream
Stream outputStream = context.HttpContext.Response.OutputStream;
if (EncodedImageBytes != null)
{
outputStream.Write(EncodedImageBytes, 0, EncodedImageBytes.Length);
}
else
{
ImageUtil.SaveImageToStream(outputStream, Image, ImageFormat, Quality);
}
}
}