Reload video after new source file in MVC website - asp.net-mvc

I am working on a web service (using ASP.NET MVC), where I need to display two videos. These two videos are regularly updated.
The models used in the MVC project simply store the path to the videos (would it be a better idea to have the video files in the model?).
I expected that, after changing the video files (new .mp4 files, keeping the old path) and reloading, the videos would change, but this is not the case.
How can I make my videos update? So far I have tried creating an HTML function with "load()", similarly as in this link, but it does not work. (Also I am not sure this would be a good solution for me, since I need to force the update from the controller.)
Thank you!
Edit: Adding some code, as requested in the comments.
The model (yes, the name is old, I will change it to "video"):
public class Picture
{
public int PictureID { get; set; }
public string FilePath { get; set; }
public string PicName { get; set; }
public int Xsize { get; set; }
public int Ysize { get; set; }
public Picture()
{
FilePath = "../../VideoDatabase/";
PicName = "Default";
Xsize = 320;
Ysize = 200;
}
public void LinkNameAndPath()
{
FilePath = "../../VideoDatabase/" + PicName + ".mp4";
}
}
In the controller, creating a model object:
Picture CreateDefault(string pictureName)
{
Picture picture = new Picture();
picture.FilePath += "Candidates/" + pictureName + ".mp4";
picture.PicName = pictureName;
return picture;
}
In the controller, calling the view:
public ActionResult Index()
{
if (candidates.Count != 2)
{
return RedirectToAction("UnexpectedError");
}
else
{
return View(candidates);
}
}
In the view, the video object:
<video id="candidate0video" width="432" height="240" controls autoplay loop>
<source src=#Model[0].FilePath type="video/mp4">
Your browser does not support the video tag.
</video>

You might want to debug your view model first. That way, you will be able to tell whether or not the view model is getting the correct values. If the file path is not updated correctly, then obviously the video won't display right on the page.
You probably should also check these things as well
Did the pictureName parameter get passed in successfully?
What about the method LinkNameAndPath()? Did it actually get called or not?
Update
public ActionResult Index()
{
var path = HostingEnvironment.MapPath("[Your file path]");
HttpContext.Response.AddHeader("Content-Disposition", "attachment; filename=[your file name].mp4");
var objFile = new FileInfo(path);
var stream = objFile.OpenRead();
var objBytes = new byte[stream.Length];
stream.Read(objBytes, 0, (int)objFile.Length);
HttpContext.Response.BinaryWrite(objBytes);
}
<video id="candidate0video" width="432" height="240" controls autoplay loop>
<source src=#Url.Action("Index","[your controller name]") type="video/mp4">
<!-- or try the following -->
<!-- <source src=#Url.Content(Model[0].FilePath) type="video/mp4"> -->
Your browser does not support the video tag.
</video>

A friend of mine offered useful insight into the matter. I translate (and adapt) here what he said, which is the base of my solution to the problem (basically solved by using new names for the files that were updated):
It is complicated. The first time you download an asset, the server adds a header HTTP "Cache-control" to tell the client to cache locally the file during some given time.
The idea is that you want active caches to reduce the load on your server. (Maybe in a development environment it makes sense to deactivate the cache.)
The typical solution is to change the name of the files automatically adding the hash of the contents of the file. This way, if you have a text file (CSS/JS) clients will download the new version when you update the content.
I recommend you to look for a library that manages that automatically (supposing the videos are uploaded through your app and not using FTP directly).

Related

Open PDF in a new tab in browser

Never done this before so not sure what is involved in it. I did search and found many answers but they were more complex than what I need. For example they needed to zoom in, generate, create accurate thumbnail, embed the actual PDF in the webpage, etc... But my question is much simpler:
If my guy that I am showing his information on webpage has some PDF to show I just want to put a generic PDF icon on the page, people click on it and the actual PDF opens in a new tab in their browser.
What is involved in just doing that? It is not like a file path, the PDF is saved in SQL server as binary objects or whatever it does to save it in SQL Server..it is not a file disk path on server
Your tags indicate asp.net-mvc.
Create a controller to handle requests for the PDF file
Pseudo:
[RoutePrefix("Pdf")]
public class PdfController : Controller {
[Route("{id}"]
public ActionResult GetPDF(int id) {
//...Code to extract pdf from SQLServer and store in stream
Stream stream = GetDataFromSQLServerById(id);
return File(stream,"filename.pdf");
}
}
On client
<a href="/Pdf/123456" target="_blank">
<img src="images/pdficon.jpg">
</a>
Update:
Referencing #ChrisPratt's comment; (which I forgot to include in my answer)
The target attribute on the anchor tag is what will tell the browser to open the link in a new tab.
Create a Controller action to for your link
public PdfResult GetPdf(int databaseRecordId)
{
var dbRecord = your code to return the sql record.
return new PdfResult(dbRecord.PdfBytes, "whatever name you want to show.pdf");
}
public class PdfResult : FileResult
{
private const String DefaultFileName = "file.pdf";
private readonly Byte[] _byteArray;
public PdfResult(Byte[] byteArray, String fileName = DefaultFileName)
: base(MediaTypeNames.Application.Pdf)
{
_byteArray = byteArray;
FileDownloadName = fileName;
}
protected override void WriteFile(HttpResponseBase response) { response.BinaryWrite(_byteArray); }
}
view code
<a href="<controller/action/databaseRecordId>" target="_blank">
<img src="<image-path>">
</a>

What templating library can be used with Asp .NET MVC?

In my MVC 5 app I need to be able to dynamically construct a list of fully qualified external URL hyperlinks, alone with some additional data, which will come from the Model passed in. I figure - I will need to construct my anchor tags something like this:
{{linkDisplayName}}
with AngularJS this would be natural, but, I have no idea how this is done in MVC.
Is there a templating library that can be used for this?
1) Create a model to Hold the Links
public class LinkObject
{
public string Link { get; set; }
public string Description { get; set; }
}
2) In your Action you can use ViewBag, ViewData or even pass the list inside you Model. I will show you how to do using ViewBag
public ActionResult MyDynamicView()
{
//Other stuff and code here
ViewBag.LinkList = new List<LinkObject>()
{
new LinkObject{ Link ="http://mylink1.com", Description = "Link 1"},
new LinkObject{ Link ="http://mylink2.com", Description = "Link 2"},
new LinkObject{ Link ="http://mylink3.com", Description = "Link 3"}
};
return View(/*pass the model if you have one*/);
}
3) In the View, just use a loop:
#foreach (var item in (List<LinkObject>)ViewBag.LinkList)
{
#item.Description
}
Just create a manual one for that, no need to do it from a template. For example, in javascript
function groupAnchor(url,display){
var a = document.createElement("a");
a.href = url;
a.className = "list-group-item";
a.target = "_blank";
a.innerHTML = display;
return a;
}
And then use that function to modify your html structure
<div id="anchors"></div>
<script>
document.getElementById("anchors").appendChild(groupAnchor("http://google.com","Google"));
</script>
Your approach to modification will more than likely be more advanced than this, but it demonstrates the concept. If you need these values to come from server side then you could always iterate over a set using #foreach() and issue either the whole html or script calls there -- or, pass the set from the server in as json and then use that in a function which is set up to manage a list of anchors.
To expand on this, it is important to avoid sending html to the view from a razor iteration. The reason being that html constructed by razor will increase the size of the page load, and if this is done in a list it can be a significant increase.
In your action, construct the list of links and then serialize them so they can be passed to the view
public ActionResult ViewWithLinks()
{
var vm = new ViewModel();
vm.Links = Json(LinkSource.ToList()).Data;
//or for a very simple test for proof of concept
var Numbers = Json(Enumerable.Range(0,100).ToList()).Data;
ViewData["numbers"] = Numbers ;
return View(vm);
}
where all you need is an object to hold the links in your view model
public class ViewModel
{
public ICollection<Link> Links { get; set; }
}
public class Link
{
public string text { get; set; }
public string href { get; set; }
}
and then in your view you can consume this json object
var allLinks = #Html.Raw(Json.Encode(Model.Links));
var numbersList = #Html.Raw(Json.Encode(ViewData["linkTest"]));//simple example
Now you can return to the above function in order to place it on the page by working with the array of link objects.
var $holder = $("<div>");
for(var i = 0; i < allLinks.length; i++){
$holder.append(groupAnchor(allLinks[i].href,allLinks[i].text));
}
$("#linkArea").append($holder);
The benefit is that all of this javascript can be cached for your page. It is loaded once and is capable of handling large amounts of links without having to worry about sending excessive html to the client.

ASP.NET MVC 3 Preview Image

I'm using MVC 3 and using the AjaxUpload plugin to upload an image using AJAX. I don't want to save the image in the file system, instead save it to the session object and then output the stream to populate an image control on the form? Would anyone know how to do this?
No idea why would ever want to do that (store the file in session) because if you have lots of users uploading their files at the same time, storing those files in the memory of your web server, especially if those files are big, won't make this server last very long. Storing the file on the filesystem is the recommended approach.
But anyway, here's how you could do it (assuming you didn't read or cared about my previous remark):
[HttpPost]
public ActionResult Upload(MyViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var buffer = new byte[model.File.InputStream];
model.File.InputStream.Read(buffer, 0, buffer.Length);
Session["uploadedFile"] = buffer;
return View(model);
}
where the File property on the view models is a HttpPostedFileBase. Next you could have a controller action which will serve this file:
public ActionResult Image()
{
byte[] buffer = (byte[])Session["uploadedFile"];
return File(buffer, "image/png");
}
and in the view you will have an <img> tag pointing to this action:
<img src="#Url.Action("image")" alt="" />
Now of course the AjaxUpload plugin allows you to upload the file using AJAX, so you don't need to reload the entire page. So in this case your controller action could simply return a JSON object to indicate whether the upload process succeeded and then in the success callback set the src property of the <img> tag to the controller action that will serve the file.
SomeView.cshtml:
<img src="#Url.Action("/Image/Render")" />
ImageController.cs:
public ActionResult Render() {
return File((byte[])Session["Avatar"], "image/jpeg")
}
Some example code. Modify it to whatever you want to do. Not really a good idea to sling an image into a session if lots of users. Better to stick it into a db if short lived, or if long lived, a more permanent storage (filesystem maybe).
public ActionResult UploadImage()
{
foreach (string imageName in Request.Files)
{
HttpPostedFileBase file = Request.Files[imageName];
if (file.ContentLength > 0)
{
BinaryReader br = new BinaryReader(file.InputStream);
byte[] content = br.ReadBytes(file.ContentLength);
Session[imageName] = content; // better to store in a db here
}
}
return View();
}
// return the image (controller action) /mycontroller/ViewImage?imageName=whatever
public FileStreamResult ViewImage(string imageName)
{
byte[] content = (byte[])Session[imageName] ; // where ever your content is stored (ideally something other than session)
MemoryStream ms = new MemoryStream(content);
return new FileStreamResult(ms, "application/octet-stream"); // set content type based on input image, it might be png, jpg, gif etc.,
}
Hope this helps.

Easiest way of porting html table data to readable document

Ok,
For the past 6 months i've been struggeling to build a system that allows user input in form of big sexy textareas(with loads of support for tables,list etc). Pretty much enables the user to input data as if it were word. However when wanting to export all this data I haven't been able to find a working solution...
My first step was to try and find a reporting software that did support raw HTML from the data source and render it as normal html, worked perfectly except that the keep together function is awful, either data is split in half(tables,lists etc) which I dont want. Or report always skips to the next page to avoid this, ending up in 15+ empty pages within the final document.
So Im looking for some kind of tip/direction to what would be the best solution to export my data into a readable document(pdf or word pref).
What I got is the following data breakdown, where data is often raw html.
-Period
--Unit
---Group
----Question
-----Data
What would be the best choice? Trying to render html to pdf or rtf? I need tips :(
And also sometimes the data is 2-3 pages long with mixed tables lists and plain text.
I would suggest that you try to keep this in the browser, and add a print stylesheet to the HTML to make it render one way on the screen and another way on paper. Adding a print stylesheet to your HTML is as easy as this:
<link rel="stylesheet" media="print" href="print.css">
You should be able to parse the input it with something like Html Agility Pack and transform it (i.e. with XSLT) to whatever output format you want.
Another option is to write HTML to the browser, but with Content-Type set to a Microsoft Word-specific variant (there are several to choose from, depending on the version of Word you're targeting) should make the browser ask if the user wants to open the page with Microsoft Word. With Word 2007 and newer you can also write Office Open XML Word directly, since it's XML-based.
The content-types you can use are:
application/msword
For binary Microsoft Word files, but should also work for HTML.
application/vnd.openxmlformats-officedocument.wordprocessingml.document
For the newer "Office Open XML" formats of Word 2007 and newer.
A solution you could use is to run an application on the server using System.Diagnostics.Process that will convert the site and save it as a PDF document.
You could use wkhtmltopdf which is an open source console program that can convert from HTML to PDF or image.
The installer for windows can be obtained from wkhtmltox-0.10.0_rc2 Windows Installer (i368).
After installing wkhtmltopdf you can copy the files in the installation folder inside your solution. You can use a setup like this in the solution:
The converted pdf's will be saved to the pdf folder.
And here is code for doing the conversion:
var wkhtmltopdfLocation = Server.MapPath("~/wkhtmltopdf/") + "wkhtmltopdf.exe";
var htmlUrl = #"http://stackoverflow.com/q/7384558/750216";
var pdfSaveLocation = "\"" + Server.MapPath("~/wkhtmltopdf/pdf/") + "question.pdf\"";
var process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = wkhtmltopdfLocation;
process.StartInfo.Arguments = htmlUrl + " " + pdfSaveLocation;
process.Start();
process.WaitForExit();
The htmlUrl is the location of the page you need to convert to pdf. It is set to this stackoverflow page. :)
Its a general question, but two things come to mind the Visitor Pattern and Changing the Mime Type.
Visitor Pattern
You can have two seperate rendering techniques. This would be up to your implementation.
MIME Type
When the request is made write date out in the Response etc
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Charset = "utf-16";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("windows-1250");
HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.doc", filename));
HttpContext.Current.Response.ContentType = "application/msword";
HttpContext.Current.Response.Write("-Period");
HttpContext.Current.Response.Write("/n");
HttpContext.Current.Response.Write("--Unit");
HttpContext.Current.Response.Write("/n");
HttpContext.Current.Response.Write("---Group");
HttpContext.Current.Response.Write("/n");
HttpContext.Current.Response.Write("----Question");
HttpContext.Current.Response.Write("/n");
HttpContext.Current.Response.Write("-----Data");
HttpContext.Current.Response.Write("/n");
HttpContext.Current.Response.End();
Here is another option, use print screens (Although it doesnt take care of scrolling, I think you should be able to build this in). This example can be expanded to meet the needs of your business, although it is a hack of sorts. You pass it a URL it generates an image.
Call like this
protected void Page_Load(object sender, EventArgs e)
{
int screenWidth = Convert.ToInt32(Request["ScreenWidth"]);
int screenHeight = Convert.ToInt32(Request["ScreenHeight"]);
string url = Request["Url"].ToString();
string bitmapName = Request["BitmapName"].ToString();
WebURLToImage webUrlToImage = new WebURLToImage()
{
Url = url,
BrowserHeight = screenHeight,
BrowserWidth = screenWidth,
ImageHeight = 0,
ImageWidth = 0
};
webUrlToImage.GenerateBitmapForUrl();
webUrlToImage.GeneratedImage.Save(Server.MapPath("~") + #"Images\" +bitmapName + ".bmp");
}
Generate an image from a webpage.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.IO;
public class WebURLToImage
{
public string Url { get; set; }
public Bitmap GeneratedImage { get; private set; }
public int ImageWidth { get; set; }
public int ImageHeight { get; set; }
public int BrowserWidth { get; set; }
public int BrowserHeight { get; set; }
public Bitmap GenerateBitmapForUrl()
{
ThreadStart threadStart = new ThreadStart(ImageGenerator);
Thread thread = new Thread(threadStart);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return GeneratedImage;
}
private void ImageGenerator()
{
WebBrowser webBrowser = new WebBrowser();
webBrowser.ScrollBarsEnabled = false;
webBrowser.Navigate(Url);
webBrowser.DocumentCompleted += new
WebBrowserDocumentCompletedEventHandler(webBrowser_DocumentCompleted);
while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
Application.DoEvents();
webBrowser.Dispose();
}
void webBrowser_DocumentCompleted(object sender,
WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser webBrowser = (WebBrowser)sender;
webBrowser.ClientSize = new Size(BrowserWidth, this.BrowserHeight);
webBrowser.ScrollBarsEnabled = false;
GeneratedImage = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height);
webBrowser.BringToFront();
webBrowser.DrawToBitmap(GeneratedImage, webBrowser.Bounds);
if (ImageHeight != 0 && ImageWidth != 0)
GeneratedImage =
(Bitmap)GeneratedImage.GetThumbnailImage(ImageWidth, ImageHeight,
null, IntPtr.Zero);
}
}

ASP.NET MVC Dynamically generated image URLs

I have an ASP.NET MVC application where I am displaying images.
These images could be located on the file system or inside a database. This is fine as I can use Url.Action in my image, call the action on my controller and return the image from the relevant location.
However, I want to be able to support images stored in Amazon S3. In this case, I don't want my controller action to return the image, it should instead generate an image URL for Amazon S3.
Although I could just perform this logic inside my view e.g.
<%if (Model.Images[0].ImageLocation == ImageLocation.AmazonS3) {%>
// render amazon image
I need to ensure that the image exists first.
Essentially I need to pass a size value to my controller so that I can check that the image exists in that size (whether it be in the database, file system or amazon s3). Once I am sure that the image exists, then I return the URL to it.
Hope that makes sense,
Ben
Try the following approach.
A model class for an image tag.
public class ImageModel
{
public String Source { get; set; }
public String Title { get; set; }
}
Helper
public static String Image(this HtmlHelper helper, String source, String title)
{
var builder = new TagBuilder("img");
builder.MergeAttribute("src", source);
builder.MergeAttribute("title", title);
return builder.ToString();
}
View with Model.Images of type IEnumerable<ImageModel>
...
<%= Html.Image(Model.Images[0].Source, Model.Images[0].Title) %>
Action
public ActionResult ActionName(/*whatever*/)
{
// ...
var model = ...;
//...
var model0 = ImageModel();
if (Image0.ImageLocation == ImageLocation.AmazonS3)
model0.Source = "an amazon url";
else
model0.Source = Url.Action("GetImageFromDatabaseOrFileSystem", "MyController", new { Id = Image0.Id });
model0.Title = "some title";
model.Images.Add(model0);
// ...
return View(model);
}
An action is a kind of a pseudo code, however the idea should be clear.
After several iterations I have come up with a workable solution, although I'm still not convinced its the best solution.
Originally I followed Anton's suggestion and just set the image url accordingly within my controller action. This was simple enough with the following code:
products.ForEach(p =>
{
p.Images[0].Url = _mediaService.GetImageUrl(p.Images[0], 200);
});
However, I soon found that this approach did not give me the flexibility I needed. Often I will need to display images of different sizes and I don't want to use properties of my model for this such as Product.FullSizeImageUrl, Product.ThumbnailImageUrl.
As far as "Product" is concerned it only knows about the images that were originally uploaded. It doesn't need to know about how we manipulate and display them, or whether we are caching them in Amazon S3.
In web forms I might use a user control to display product details and then use a repeater control to display images, setting the image urls programatically in code behind.
I found that the use of RenderAction in ASP.NET MVC gave me similar flexibility:
Controller Action:
[ChildActionOnly]
public ActionResult CatalogImage(CatalogImage image, int targetSize)
{
image.Url = _mediaService.GetImageUrl(image, targetSize);
return PartialView(image);
}
Media Service:
public MediaCacheLocation CacheLocation { get; set; }
public string GetImageUrl(CatalogImage image, int targetSize)
{
string imageUrl;
// check image exists
// if not exist, load original image from store (fs or db)
// resize and cache to relevant cache location
switch (this.CacheLocation) {
case MediaCacheLocation.FileSystem:
imageUrl = GetFileSystemImageUrl(image, targetSize);
break;
case MediaCacheLocation.AmazonS3:
imageUrl = GetAmazonS3ImageUrl(image, targetSize);
break;
default:
imageUrl = GetDefaultImageUrl();
break;
}
return imageUrl;
}
Html helper:
public static void RenderCatalogImage(this HtmlHelper helper, CatalogImage src, int size) {
helper.RenderAction("CatalogImage", "Catalog", new { image = src, targetSize = size });
}
Usage:
<%Html.RenderCatalogImage(Model.Images[0], 200); %>
This now gives me the flexibility I require and will support both caching the resized images to disk or saving to Amazon S3.
Could do with some url utility methods to ensure that the generated image URL supports SSL / virtual folders - I am currently using VirtualPathUtility.
Thanks
Ben
You can create a HttpWebRequest to load the image. Check the header in the response, if it's 200 that means it was successful, otherwise something went wrong.

Resources