Video streaming from database - c#

I am trying to make an action to display media from database (ASP.NET MVC4):
var memoryStream = new MemoryStream(mediaContent.File.FileData.Data);
return new FileStreamResult(memoryStream, MimeMapping.GetMimeMapping(mediaContent.File.Filename));
Pictures are displaying fine, but I have problem with videos (.avi) when I am going to link mysite/media/4 in Chrome or Firefox it displays:
<embed width="100%" height="100%" name="plugin" src="http://mysite/media/4" type="video/x-msvideo">
But video is not playing (as it happens if link pointing to real video file), but if I am opening this link in IE it prompts me to download file and when I am opening this file from player it works fine.
Response headers:
Cache-Control:private, s-maxage=0
Content-Length:808680
Content-Type:video/x-msvideo
Date:Wed, 06 Nov 2013 10:03:09 GMT
Server:Microsoft-IIS/7.5
X-AspNet-Version:4.0.30319
X-AspNetMvc-Version:4.0
X-MiniProfiler-Ids:["e305dcdb-79be-4452-94d2-a9999ffaa13a","c0c81d12-8b31-425c-a57b-2ad186c958d5","1f7f3c09-a695-49f1-9203-6b5bf44b837a","fb0d637e-5926-4759-ad6f-f7322403e98c","f08c0392-10d6-4477-b2df-be52ab9a1d64","366d6122-15a5-41b4-840a-607fc6931996","11fd2eb7-efce-47a1-96f8-09fbdb0b1fa0","690e67b7-b1fb-46a3-9aa3-ef6207203f55","a51640ad-f31d-4f12-a807-6ea06ba0ee46","38adc052-9c41-4243-97d2-41dbf3b36093","9d255225-c122-44ef-8021-5b6f9d4dd549","2b249ff3-9e37-43c3-b6ab-b78b26c6d6ce","2bec0b1b-4898-4b14-bf12-cc331e27ecfc","49c72e01-c8d4-495f-af7e-8ffd687e94e9","1c87e454-f90d-49f4-9618-8dfe0d9c0329","2152a9a8-54ae-47d8-b98a-83ac32dbdb0c","9cf93254-9552-4834-826e-df7e8a7d8e73","a2d782e2-96ca-4e9c-b612-9782a37a06ca","e10ecc8a-5811-4cca-b566-3f09e1de3f2c","3769bb15-60f9-43c3-ad6c-285f3fb47112","1996c4aa-9f76-4f33-95fa-3f7f5b3e72f4"]
X-Powered-By:ASP.NET
What I am doing wrong? I want to have a link which I can use in <object> in order to display this video on page.
Update 1:
The response that I am getting if I type url for physical video file doesn't make any sence:
HTTP/1.1 304 Not Modified
Last-Modified: Tue, 05 Nov 2013 17:07:56 GMT
Accept-Ranges: bytes
ETag: "5d50369249dace1:0"
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Wed, 06 Nov 2013 11:20:32 GMT
But it works and start play video inside browser using intaled VLC player plugin.
Update 2:
I have tried different implementations to return video and tried to put link to src of object
<object id="video-player" class="preview-container" type="video/x-msvideo" src="{link to video}" loop="true" controls="false" autoplay="true"></object>
So in case if link to real video like "localhost/media/some_video.avi"
then it works fine inside object and if going to url directly.
I checked behavior for different implementations
1) return File(memoryStream, MimeMapping.GetMimeMapping(mediaContent.File.Filename), mediaContent.File.Filename);
to url: prompts to download video file;
in object: shows empty plugin;
2) return new FileContentResult(mediaContent.File.FileData.Data, "application/x-vlc-plugin")
to url: shows empty plugin;
in object: shows empty plugin;
3) return new FileContentResult(mediaContent.File.FileData.Data, "video/avi")
to url: prompts to download video file;
in object: shows empty plugin;
Update 3:
I made a HttpHandler:
public void ProcessRequest(HttpContext context)
{
MediaContent mediaContent;
//getting mediaContent
context.Response.Clear();
context.Response.Buffer = true;
context.Response.AppendHeader("Content-Disposition", "inline; filename=" + mediaContent.File.Filename);
context.Response.ContentType = MimeMapping.GetMimeMapping(mediaContent.File.Filename);
context.Response.BinaryWrite(mediaContent.File.FileData.Data);
context.Response.End();
}
And it simply WORKS. Ok. Then I made action with same logic as Handler:
[HttpGet]
public void Media(int id)
{
MediaContent mediaContent;
//getting mediaContent
Response.Clear();
Response.Buffer = true;
Response.AppendHeader("Content-Disposition", "inline; filename=" + mediaContent.File.Filename);
Response.ContentType = MimeMapping.GetMimeMapping(mediaContent.File.Filename);
Response.BinaryWrite(mediaContent.File.FileData.Data);
Response.End();
}
But this action still not working with video and I started to compare response headers.
Handler:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: video/x-msvideo
Server: Microsoft-IIS/7.5
Content-Disposition: inline; filename=Reebok_App_attract640L.avi
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 06 Nov 2013 17:02:45 GMT
Content-Length: 808680
Action:
HTTP/1.1 200 OK
Cache-Control: private, s-maxage=0
Content-Type: video/x-msvideo
Transfer-Encoding: chunked
Server: Microsoft-IIS/7.5
X-AspNetMvc-Version: 4.0
Content-Disposition: inline; filename=Reebok_App_attract640L.avi
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 06 Nov 2013 17:02:47 GMT
To get rid of Transfer-Encoding I have added Content-Length to my action:
Response.AddHeader("Content-Length", mediaContent.File.FileData.Data.Length.ToString());
I couldn't get rid of s-maxage=0, but now headers similar (except s-maxage=0, X-AspNetMvc-Version: 4.0 and header order)

Well, I did my best to mirror your initial code, minus the database, what I have below works fine for me. VS2012, MVC4, IIS Express local, IIS7.5 remote, works on IE10 local, IE10 remote, IE9 remote. The AVI file I used is some random file I found inside my windows\winsxs folder. I am going to suggest you have a client side problem (IE specifically). Maybe something like cookie handling issues (http://mvolo.com/iis-70-forms-authentication-and-embedded-media-players/), IE security zone settings or something else?
By the way height=100% on embed does not work for me, needs to be pixels.
Controller
namespace MvcApplication4.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return new ViewResult();
}
public ActionResult Media(int id)
{
string fn = Server.MapPath("~/App_Data/boxed-delete.avi");
var memoryStream = new MemoryStream(System.IO.File.ReadAllBytes(fn));
return new FileStreamResult(memoryStream, MimeMapping.GetMimeMapping(System.IO.Path.GetFileName(fn)));
}
}
}
View
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<!-- http://localhost:54941/Home/Media/3 -->
<embed width="100%" height="500" name="plugin" src="~/Home/Media/3" type="video/x-msvideo">
</div>
</body>
</html>

You can put file location stored in the database into a byte buffer array and read from the array in chunk instead of reading whole array at once
WebRequest wreq = (HttpWebRequest)WebRequest.Create(url);
using (HttpWebResponse wresp = (HttpWebResponse)wreq.GetResponse())
using (Stream mystream = wresp.GetResponseStream())
{
using (BinaryReader reader = new BinaryReader(mystream))
{
int length = Convert.ToInt32(wresp.ContentLength);
byte[] buffer = new byte[length];
buffer = reader.ReadBytes(length);
Response.Clear();
Response.Buffer = false;
Response.ContentType = "video/mp4";
while(true) {
int bytesRead = myStream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
Response.OutputStream.Write(buffer, 0, bytesRead);
}
Response.End();
}
}
Also you can check out the given url weather it helps,
http://www.devcurry.com/2013/04/play-videos-in-aspnet-mvc-html5-using.html
Thanks

Related

C# WebClient Strange Characters

I am trying to download this webpage using C# WebClient..
Now it works perfectly with python urllib2 but with c# web client it gives these strange characters in the output file..
I have tried using Encoding with webclient class as well but it doesn't work at all..
public static string GetWebURL()
{
string url = "http://bet.hkjc.com";
WebClient webClient = new WebClient();
webClient.Encoding = Encoding.UTF8;
string html = webClient.DownloadString(url);
File.WriteAllText("page.html", html);
}
this is the output with those strange characters
‹âå²Qtñw‰pUðñõQuòñtVPÒÕ×7vÖ×w qÂH˜è*„%æg–dæç%æèë»ú)ÙñrÂ(N.Ê,(Q(©,HµU*I­(ÑÃJ,K„ˆ*Ùq)((â€U*TÆ’e‰E ©y‰I9©ŽÉÉ©ÅÅÎùy%Eù9 ¶i‰9Å©Ö %â„¢i Xâ€h"(É-P°U(ÃÃŒKÉ/×ËÉON¹H/£(5M¯¸4©¸¤HÃ\SlHu°kPËœkP¼Ÿ£¯+PP/L‘ÂËœ4&µÂ?MCI_IS®+%?713Ã/17¨ ɘfd!¸ zJšÚ†P«Sò“KsSóJô &MA V¨ŸKòô’RK‚s2ÜŠ€ªô2‹}òÓóó445¡ÊÃ=­Wâ€Z“˜œ t|zj^jQbN<Ø1z䁚9‰y鶩yJ_ÂP-ˆÔšœchˆe¦‚ µ\H&[×rÙèC’€0ÂJ%à „ ÷‚üüP9Ud¦MÃÃÔÌØÈÖM×ÃÈ25² ÷ô³V·†(ÃŽM-JOM
What should I do to see the html that is being send?
You're looking at a compressed byte stream. You can tell by inspecting the headers of the http response, for example with curl:
curl -X HEAD -i http://bet.hkjc.com/
but the Developer Console of your browser will reveal the same:
HTTP/1.1 200 OK
Cache-Control: public, max-age=120, must-revalidate
Content-Length: 3615
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Expires: Wed, 29 Jun 2016 08:01:06 GMT
Vary: Accept-Encoding
Server: Microsoft-IIS/7.0
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Wed, 29 Jun 2016 08:00:14 GMT
Via: 1.1 stjbwbwa52
Accept-Ranges: bytes
Notice the Content-Encoding: to say gzip. This means the result you just got is compressed with the gzip algorithm. The standard WebClient can't handle that but with an simple subclass the WebClient can do new tricks:
public class DecompressWebClient:WebClient
{
// moved common logic here
public DecompressWebClient()
{
this.Encoding = Encoding.UTF8;
}
// This is the factory to create the webrequest
protected override WebRequest GetWebRequest(Uri address)
{
// get the default one
var request = base.GetWebRequest(address);
// see if it is a HttpWebRequest
var httpReq = request as HttpWebRequest;
if (httpReq != null)
{
// add extra capabilities, like decompression
httpReq.AutomaticDecompression = DecompressionMethods.GZip;
}
return request;
}
}
On the HttpWebRequest there exists a property AutomaticDecompression that, when set to true, will take care of the decompression for us.
When you put the Subclassed WebClient to use your code will look like:
string url = "http://bet.hkjc.com";
using(WebClient webClient = new DecompressWebClient())
{
string html = webClient.DownloadString(url);
File.WriteAllText("page.html", html);
}
The encoding UTF8 is correct, as you can also see in the header for the Content-Type setting.
The top of the html file will look like this:
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE10"/>
<meta name="application-name" content="香港賽馬會"/>
<title>香港賽馬會</title>

How to download file in C# with Content-Length: 0

I tried WebClient, HttpWebRequest, WebRequest and couple other ways to download file from specific server but every time the file is empty (0 byte). I discovered that in the response headers:
Pragma: Public
Content-Disposition: attachment; filename="hQPDAU0.mp3"
Content-Transfer-Encoding: binary
Connection: close
Accept-Ranges: bytes
Content-Length: 0
Cache-Control: max-age=1468800
Content-Type: audio/mpeg
Date: Tue, 08 Jul 2014 08:52:05 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Set-Cookie: sessioncode=4v0jgqiq.....1kulouk0c01; path=/; domain=.domain
Server: Apache/2.2.15 (CentOS)
X-Powered-By: PHP/5.4.29
the Conent-Length is 0. I've opened the URL in my browser and it forced it to download file. But how can I download file in C#?
Pass the URL to the WebMethod hope it works for you.
[WebMethod]
public static string ProcessIT(string downloadURL, string file_name)
{
// Create a new WebClient instance.
WebClient myWebClient = new WebClient();
string path = #"c:\";
string path_n_name = path + file_name;
// Download the Web resource and save it into the current filesystem folder.
myWebClient.DownloadFile(downloadURL, path_n_name);
return "SUCCESS";
}
public FileResult DownloadExcel(string filepath)
{
byte[] fileBytes = System.IO.File.ReadAllBytes(filepath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet,
`enter code here`Path.GetFileName(filepath));
}
------------------------------------------------------------------------

Caching ASHX Image Response

I have created an ashx file to generate image thumbnails on the fly. I would like to cache these images client side after they are called the first time.
URL:
~/image.ashx?dir=user&w=25&h=25&force=yes&img=matt.jpg
Code Behind:
public void ProcessRequest (HttpContext context) {
TimeSpan refresh = new TimeSpan(0, 15, 0);
context.Response.Cache.SetExpires(DateTime.Now.Add(refresh));
context.Response.Cache.SetMaxAge(refresh);
context.Response.Cache.SetCacheability(HttpCacheability.Server);
context.Response.CacheControl = HttpCacheability.Public.ToString();
context.Response.Cache.SetValidUntilExpires(true);
string dir = context.Request.QueryString["dir"];
string img = context.Request.QueryString["img"];
bool force = context.Request.QueryString["force"] == "yes";
double w = 0;
double h = 0;
...
context.Response.ContentType = "image/jpeg";
thumb.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
Using the Dev Tools in Chrome I can see the following Response Header:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 26 Sep 2011 19:17:31 GMT
X-AspNet-Version: 4.0.30319
Set-Cookie: .ASPXAUTH=...; path=/; HttpOnly
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: image/jpeg
Content-Length: 902
Connection: Close
Can someone enlighten me as to why this isn't caching on multiple calls with the same exact URL? Any tips would be greatly appreciated.
Surely this line:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Server);
Should be:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);

Cache.SetMaxAge not working under IIS, works fine under VS Dev Srv

I'm trying to add a "max-age" header to my response. It works fine on my Visual Studio Development Server, but as soon as I move the app to IIS (tried both IIS express locally and IIS on the server) - the header disappears.
My code:
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(new TimeSpan(1, 0, 0, 0));
VS Dev server response (all works just fine):
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Fri, 07 Jan 2011 14:55:04 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: public, max-age=86400
IIS7 Response
HTTP/1.1 200 OK
Server: Microsoft-IIS/7.5
Date: Fri, 07 Jan 2011 15:00:54 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: public
PS. It's an ASHX-handler, if it matters...
UPDATE: 2011-03-14 The fix is ensure you call SetSlidingExpiration(true)
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(5));
context.Response.ContentType = "image/jpeg";
context.Response.Cache.SetSlidingExpiration(true);
If you remove the OutputCache module you will get the desired result. I see this as a bug.
So, in your web.config you would do the following:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="OutputCache"/>
</modules>
</system.webServer>
ADDED: So, there's additional information.
Using MVC's OutputCacheAttribute apparently doesn't have this issue
Under the same MVC application, without removing "OutputCache" from the modules, a direct implementation if IHttpHandler or an ActionResult results in the s-maxage being stripped
The following strips the s-maxage
public void ProcessRequest(HttpContext context)
{
using (var image = ImageUtil.RenderImage("called from IHttpHandler direct", 5, DateTime.Now))
{
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(5));
context.Response.ContentType = "image/jpeg";
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
The following strips the s-maxage
public ActionResult Image2()
{
MemoryStream oStream = new MemoryStream();
using (Bitmap obmp = ImageUtil.RenderImage("Respone.Cache.Setxx calls", 5, DateTime.Now))
{
obmp.Save(oStream, ImageFormat.Jpeg);
oStream.Position = 0;
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(TimeSpan.FromMinutes(5));
return new FileStreamResult(oStream, "image/jpeg");
}
}
This does NOT - go figure...
[OutputCache(Location = OutputCacheLocation.Any, Duration = 300)]
public ActionResult Image1()
{
MemoryStream oStream = new MemoryStream();
using (Bitmap obmp = ImageUtil.RenderImage("called with OutputCacheAttribute", 5, DateTime.Now))
{
obmp.Save(oStream, ImageFormat.Jpeg);
oStream.Position = 0;
return new FileStreamResult(oStream, "image/jpeg");
}
}
Solution:
in web.config:
<staticContent>
<clientCache cacheControlCustom="public" cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00"/>
</staticContent>
and en IIS PC:
With cmd go to c:\windows\system32\inetsrv.
Then execute:
appcmd unlock config /section:staticContent
Belated answered but this could help someone :-
Response.Cache.SetProxyMaxAge(TimeSpan.Zero);

Unable to open download save dialog

Using the below code I am unable to show the open/save as file dialog:
public void ProcessRequest(HttpContext context)
{
string link = context.Request.QueryString["Link"];
string extension = Path.GetExtension(link);
string fileName = Path.GetFileName(link);
string fullPath =
String.Format("{0}\\{1}",
context.Server.MapPath("~/Content/Uploads/"),
fileName);
if (File.Exists(fullPath))
{
context.Response.ClearContent();
context.Response.ClearHeaders();
context.Response.AddHeader(
"Content-Length",
new FileInfo(fullPath).Length.ToString());
string contentType;
switch (extension)
{
default:
contentType = "application/octet-stream";
break;
}
context.Response.ContentType = contentType;
context.Response.AddHeader(
"Content-Disposition",
String.Format("attachment; filename={0}", fileName));
context.Response.WriteFile(fullPath, true);
context.Response.Flush();
}
}
I've tried to close the response, leave the response open, use TrasmitFile(), but I never get any dialog or any feed back whatsoever. I've tried debugging it as well, but no exceptions are being thrown. Tried in IE 7/8, and Chrome. Any help is appreciated.
Thanks!
Below is the Fiddler output:
HTTP/1.1 200 OK Cache-Control: private
Content-Length: 3813 Content-Type:
application/octet-stream Server:
Microsoft-IIS/7.5 Content-Disposition:
attachment;
filename=b1af9b34-28cc-4479-a056-8c55b41a5ece.txt
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET Date: Thu, 23
Dec 2010 21:51:58 GMT
* Home
* Hotels
* Reviews
* Community
* Travel Guide
* Travel Insurance
* Contact us
* FIDDLER: RawDisplay truncated at 128 characters. Right-click to disable
truncation. *
Finally figured it out. There is actually no problem with the code I posted. As you can see in the Fiddler output, the contents of the text file were successfully written to the response stream and the headers used were also correct. The actual problem comes from how the actual http request was made. I used a
$.get(urlToGenericHandler);
request using jQuery. The reason why specifically I am not able to download a file using AJAX or a callback model is beyond the scope of this answer. See supported jQuery datatypes here
Anyways, I changed the call from using AJAX to using a basic post-back.
Thanks to all that helped.
Try changing
contentType = "application/octet-stream";
to
contentType = "application/download";
Update:
Try swapping the position of the header and content type
context.Response.AddHeader(
"Content-Disposition",
String.Format("attachment; filename={0}", fileName));
context.Response.ContentType = contentType;
context.Response.AddHeader(
"Content-Length",
new FileInfo(fullPath).Length.ToString());

Categories