Streaming MP3 Chunks on ASP.NET - c#

Currently, I have a feature on an ASP.NET website where the user can play back MP3 Files. The code looks something like this:
Response.Clear();
Response.ContentType = "audio/mpeg";
foreach (DataChunk leChunk in db.Mp3Files.First(mp3 => mp3.Mp3ResourceId.Equals(id)).Data.Chunks.OrderBy(chunk => chunk.ChunkOrder))
{
Response.BinaryWrite(leChunk.Data);
}
Unfortunately, if a larger MP3 file is selected, the audio does not begin to play until the entire file is downloaded, which can cause a noticeable delay. Is there any way to get the MP3 to start playing immediately, even though the entire file may not yet be transferred?

You should be able to do what you want by writing to the outpstream of the response, i.e.:
Response.OutputStream.Write
It is also probably a good idea to check previously if Response.IsClientConnected and give up if not.
I found a demo that allows playback of mp3 files from an asp.net web application:
http://aspsnippets.com/Articles/Save-MP3-Audio-Files-to-database-and-display-in-ASPNet-GridView-with-Play-and-Download-option.aspx

try this:
Response.BufferOutput = false; //sets chunked encoding
Response.ContentType = "audio/mpeg";
using (var bw = new BinaryWriter(Response.OutputStream))
{
foreach (DataChunk leChunk in db.Mp3Files.First(mp3 => mp3.Mp3ResourceId.Equals(id)).Data.Chunks.OrderBy(chunk => chunk.ChunkOrder))
{
if (Response.IsClientConnected) //avoids the host closed the connection exception
{
bw.Write(leChunk.Data);
}
}
}
Also, go yo your web.config file and do this if you still have problems with chunked encoding:
<system.webServer>
<asp enableChunkedEncoding="true" />
</system.webServer>
The error you reported above about the host being closing the connection is happening probably because you are opening the page using the browser and when the browser reads the content type, it opens the media player and closes itself who had the opened connection which was then closed, causing that error, so to avoid this, you need to check periodically whether your client is still connected or not.
Finally, I would use a Generic Handler (.ashx) or a custom handler and set a .mp3 extension for this if you are using a aspx page to avoid the unnecessary overhead of the web page.
I hope this helps.

Try setting Response.BufferOutput = false before streaming the response.

If the location of the MP3 files are publicly available to your user then an alternative approach could be to just return the MP3's URL and use the HTML 5 audio tags in your mark up to stream the music. I am pretty sure that the default behaviour of the audio tag would be to stream the file rather than wait until the whole file has downloaded.

One method to support this would be implementing HTTP byte range requests.
By default I don't believe that ASP.NET does this, and definitely won't if using any of the code in the questions or the answer.
You can implement this manually with a little work though. Another option, which would be much less dev work, would be to let IIS serve a static file. I assume that isn't an option though.
Here's an example implementation:
http://www.codeproject.com/Articles/820146/HTTP-Partial-Content-In-ASP-NET-Web-API-Video

Related

Serve Image with IHttpHandler

I am working with a web server that is only available on our internal network. I have a need to make an image it serves available to the public via our DMZ web server. The internal system has a GUI where anyone can upload any type of image so my HttpHandler must be flexible.
The bit of code I am currently using is:
WebResponse webResponse = WebRequest.Create(imagePath).GetResponse();
using (Stream stream = webResponse.GetResponseStream())
{
context.Response.Clear();
context.Response.ContentType = webResponse.ContentType;
stream.CopyTo(context.Response.OutputStream);
context.Response.End();
}
This seems to work and it serves up images correctly on HTML pages, but when I right click in Chrome and select "Open image in new tab" I get garbled up text like this:
Any suggestions to make my code better or is this okay?
Thanks!
EDIT: Seems like this code snippet does exactly what it should do. The problem was with the internally hosted image itself. It Served the exact same garbled text even before running through this HttpHandler.
Your content type header is probably not being set correctly.
Change it to context.Response.ContentType = "image/png";, but replace "png" with whatever the image time you are loading happens to be.

100s of concurrent users trying to download files (asp.net C# application)

I am trying to implement file download feature in asp.net application. The application would be used by say around 200 users concurrently to download various files.
It would be hosted on IIS 7. I do not want the application server to crash because of multiple requests coming concurrently.
I am assuming that by calling Context.Response.Flush() in a loop, I am flushing out all the file data that I would have read till then, so application memory usage would be kept uniform. What other optimizations can I make to the current code or what other approach should be used in a scenario like this?
The requests would be for various files and the file sizes can be anywhere between 100 KB to 10 MB.
My current code is like this:
FileStream inStr = null;
byte[] buffer = new byte[1024];
String fileName = #"C:\DwnldTest\test.doc";
long byteCount; inStr = File.OpenRead(fileName);
Response.AddHeader("content-disposition", "attachment;filename=test.doc");
while ((byteCount = inStr.Read(buffer, 0, buffer.Length)) > 0)
{
if (Context.Response.IsClientConnected)
{
Context.Response.ContentType = "application/msword";
//Context.Response.BufferOutput = true;
Context.Response.OutputStream.Write(buffer, 0, buffer.Length);
Context.Response.Flush();
}
}
You can use Response.TransmitFile to save server memory when sending files.
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename=testdoc.pdf");
Response.TransmitFile(#"e:\inet\www\docs\testdoc.pdf");
Response.End();
In your code example, you're not closing / disposing inStr. That could affect performance.
Another more simple way to do this would be to use the built in method:
WriteFile
It should already be optimized and will take care of opening / closing files for you.
Maybe you want to use FileSystemWatcher class to check if the file was modified, and read it into memory only while such change was detected. For rest of the time just return the byte array that is already stored in memory. I don't know if HttpResponse.WriteFile method is sensitive for such file modification changes, or if always reads a file from given path, but this also seems to be a good option to use, as it is served by framework out of the box.
Since you are sending an existing file to the client, consider using HttpResponse.TransmitFile (http://msdn.microsoft.com/en-us/library/12s31dhy.aspx).
Looking at the .NET code it seems that this will forward the file writing to IIS instead of reading/writing it in ASP.NET process. HttpResponse.WriteFile(string, false) and HttpResponse.Write(string) seems to do the same thing.
In order to verify that the file sending is relayed to IIS, at HttpResponse.Output property - it should be of type HttpWriter. The HttpWriter._buffers array should now contain a new element HttpFileResponseElement).
Of course, you should always investigate if caching is appropriate in your scenario and test if it is being used.

How to identify whether a download is completed using an ashx handler

In one of our project we need the functionality to download a file from server to client location.
For this we are using an ashx handler to do the operation. Its working perfectly and we are able to download files.
Now we need a requirement like we need to update a field when a download is started and completed. Is there any way to do this.
Once we click the download link the Save as dialog box will appear and after that i think we don't have any control to check the progress. I think we even don't know which button is clicked ie we don't know whether the user is clicked a 'Yes' or 'No'.
Can anyone please suggest a method to know when the download is started and when it has been completed? We are using Asp.Net 2.0 with c#.
The handler used for download is given below
string fileUrl = string.Empty;
if (context.Request["fileUrl"] != null)
{
fileUrl = context.Request["fileUrl"].ToString();
}
string filename = System.IO.Path.GetFileName(fileUrl);
context.Response.ClearContent();
context.Response.ContentType = "application/exe";
context.Response.AddHeader("content-disposition", String.Format("attachment; filename={0}", filename));
context.Response.TransmitFile(fileUrl);
context.Response.Flush();
The file is downloaded from an aspx page method like
private void DownloadExe()
{
string downloadUrl = "Test.exe");
Response.Redirect("Test.ashx?fileUrl=" + downloadUrl, false);
}
Your ASHX handler knwos if download started (since it is actually get called) and when download is completed (end of handler is reached). You may even get some progress server side if you are writing response manually in chunks, this way you also may be able to detect some cases when user cancels download (if writing to response stream fails at some point).
Depending on your needs you may be able to transfer this information to other pages (i.e. via session state) or simply store in some database.
How about this:
Response.BufferOutput = false;
Response.TransmitFile(fileUrl);
//download complete code
If you disable response output buffering then it won't move past the line of code that sends the file to the client until the client has finished receiving it. If they cancel the download half way through it throws a HttpException so the download complete code doesn't get run.
You could also place your download complete code after your call to flush the buffer. But it's better not to enable buffering when sending large binary files to save on server memory.
Ok I had the same problem and jumped over this site:
Check over coockies
This works great for me.

C#: HttpListener Error Serving Content

I have implemented something similar to this
only real difference is
string filename = context.Request.RawUrl.Replace("/", "\\").Remove(0,1);
string path = Uri.UnescapeDataString(Path.Combine(_baseFolder, filename));
so that I can traverse to subdirectories. This works great for webpages and other text file types but when trying to serve up media content I get the exception
HttpListenerException: The I/O
operation has been aborted because of
either a thread exit or an application
request
Followed by
InvalidOperationException: Cannot close stream until all bytes are written.
In the using statement.
Any suggestions on how to handle this or stop these exceptions?
Thanks
I should mention that I am using Google Chrome for my browser (Google Chrome doesn't seem to care about the MIME types, when it sees audio it will try to use it like it's in a HTML5 player), but this is also applicable if you are trying to host media content in a page.
Anyways, I was inspecting my headers with fiddler and noticed that Chrome passes 3 requests to the server. I started playing with other browsers and noticed they did not do this, but depending on the browser and what I had hard coded as the MIME type I would either get a page of crazy text, or a download of the file.
On further inspection I noticed that chrome would first request the file. Then request the file again with a few different headers most notably the range header. The first one with byte=0- then the next with a different size depending on how large the file was (more than 3 requests can be made depending how large the file is).
So there was the problem. Chrome will first ask for the file. Once seeing the type it would send another request which seems to me looking for how large the file is (byte=0-) then another one asking for the second half of the file or something similar to allow for a sort of streaming experienced when using HTML5. I coded something quickly up to handle MIME types and threw a HTML5 page together with the audio component and found that other browsers also do this (except IE)
So here is a quick solution and I no longer get these errors
string range = context.Request.Headers["Range"];
int rangeBegin = 0;
int rangeEnd = msg.Length;
if (range != null)
{
string[] byteRange = range.Replace("bytes=", "").Split('-');
Int32.TryParse(byteRange[0], out rangeBegin);
if (byteRange.Length > 1 && !string.IsNullOrEmpty(byteRange[1]))
{
Int32.TryParse(byteRange[1], out rangeEnd);
}
}
context.Response.ContentLength64 = rangeEnd - rangeBegin;
using (Stream s = context.Response.OutputStream)
{
s.Write(msg, rangeBegin, rangeEnd - rangeBegin);
}
Try:
using (Stream s = context.Response.OutputStream)
{
s.Write(msg, 0, msg.Length);
s.Flush()
}

download a file from server and then delete on server

ok i am downloading a file from a server and i plan to delete the file that i have downloaded on the server after it gets downloaded on the client side..
My download code is working fine but i dont know when to put the command to delete the file.
string filepath = restoredFilename.ToString();
// Create New instance of FileInfo class to get the properties of the file being downloaded
FileInfo myfile = new FileInfo(filepath);
// Checking if file exists
if (myfile.Exists)
{
// Clear the content of the response
Response.ClearContent();
// Add the file name and attachment, which will force the open/cancel/save dialog box to show, to the header
Response.AddHeader("Content-Disposition", "attachment; filename=" + myfile.Name);
//Response.AddHeader("Content-Disposition", "inline; filename=" + myfile.Name);
// Add the file size into the response header
Response.AddHeader("Content-Length", myfile.Length.ToString());
// Set the ContentType
Response.ContentType = ReturnExtension(myfile.Extension.ToLower());
//// Write the file into the response (TransmitFile is for ASP.NET 2.0. In ASP.NET 1.1 you have to use WriteFile instead)
Response.TransmitFile(myfile.FullName);
// End the response
Response.End();
}
Now i know the response.End() will stop every thing and return the value, so is there another way too do so..
I need to call a function
DeleteRestoredFileForGUI(restoredFilename);
to delete the file but dont know where to put it.. i tried putting before and after Response.End() but it does not work..
any help is appreciated... thanks
Add
Response.Flush();
DeleteRestoredFileForGUI(restoredFilename);
after the call to TransmitFile() and ditch the call to Response.End() (you don't need it).
If that does not work, then ditch TransmitFile() and go with:
Stream s = myFile.OpenRead();
int bytesRead = 0;
byte[] buffer = new byte[32 * 1024] //32k buffer
while((bytesRead = s.Read(buffer, 0, buffer.Length)) > 0 &&
Response.IsClientConnected)
{
Response.OutputStream.Write(buffer, 0, bytesRead);
Response.Flush();
}
you can't delete the file straight away as it may not have been downloaded yet. from the server side there is no easy way of telling that the file was successfully downloaded. what if an open/save dialog is opened by the browser? download won't begin until the dialog is acknowledged. (this may not be immediately and/or the dialog may be cancelled)
or, what if it is a large file and the connection is dropped before it is fully downloaded? should it be possible to attempt the download again?
the normally recommended way of dealing with your situation is to do the deletion as a separate process, after a time period which allows you to be (fairly) sure the file is no longer required and/or it can be recreated/restored if need be.
depending on your situation you could have a separate process which periodically removes/processes old files. or, if you have a low volume of traffic, you could check for and delete old files each time a new one is requested.
the identification of old files will likely be based on a file time or associated value in a darabase. either way, if there are potentially lots of files to process you are unlikely to want the overhead of checking very frequently if it is unlikely to identify a lot of files to remove.
also, be sure to way up the consequences of lots of files not being removed ASAP (is disk space really an issue?) against the side effects of possibly deleting them while still needed or creating a performance side effect by checking to zealously.
The general pattern you are following makes me wonder, are you doing this?
Create Data for Client and Save to
Disk Transmit File to Client Delete
File
If you are, you might change your system to work in memory. Since memory is managed in .Net you wouldn't have to do this manual cleanup, and depending on the size of the file this could be a good bit faster too:
Create Data for Client and Save to MemoryStream
Transmit Stream to Client
Since you set the file name in the header, you have two options:
Read the file contents into a string, delete the file, echo/print the string as the body of the message.
Rename the file something like delete-filename.xxx and then have some external process (maybe a cron job?) that goes behind and deletes any files beginning with that prefix.

Categories