I have a file upload on my .NET website. I used the FileUpload .NET control for the upload, and set the target of the form to a hidden iframe. I use client-side script to submit the form, then display a progress bar on the page, which does an AJAX request to an ashx file on the same server and updates the progress bar every second.
Here's the part of the code for when the file is uploading -
string fileString = StringGenerator.GenerateString(8);
System.IO.Stream fileStream = System.IO.File.Create(fileRoot+ fileString + ".mp3");
System.IO.Stream inputStream = fileUpload.PostedFile.InputStream;
byte[] buffer = new byte[256];
int read;
if(StaticProgress.Progress.ContainsKey(trackId)) StaticProgress.Progress[trackId] = 0d;
else StaticProgress.Progress.Add(trackId, 0d);
int totalRead = 0;
long length = inputStream.Length;
while ((read = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
totalRead += read;
fileStream.Write(buffer, 0, read);
StaticProgress.Progress[trackId] = (double)totalRead / (double)length;
}
StaticProgress.Progress is a property which returns a static Dictionary.
Here is the code in the ashx file that gets the progress of the
context.Response.ContentType = "text/javascript";
int id;
if(!int.TryParse(context.Request.QueryString["id"], out id) || !StaticProgress.Progress.ContainsKey(id))
context.Response.Write("{\"error\":\"ID not found\"}");
else
context.Response.Write("{\"id\":\"" + id + "\",\"decimal\":" + StaticProgress.Progress[id] + "}");
Here I always "ID not found" while the file is uploading, but when the file has finished uploading, I get a successful - decimal : 1.
I've tried using session variables before this and got the same result. I've tried using a public static (thread safe?) Dictionary, but I get the same result. Has anyone came across anything like this before? Does the code not execute until the client request has been fully received?
The standard file upload control provides no progress abilities - it does not receive the file until the file has been fully received by the server. By the time you open the stream on the .PostedFile, the file has been completely uploaded.
There are a number of better solutions to uploading discussed here.
Related
I'm trying to play in Chrome Browser video with source from Web Api
<video id="TestVideo" class="dtm-video-element" controls="">
<source src="https://localhost:44305/Api/FilesController/Stream/Get" id="TestSource" type="video/mp4" />
</video>
In order to implement progressive downloading I'm using PushStreamContent in server response
httpResponce.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)new StreamService(fileName,httpResponce).WriteContentToStream);
public async void WriteContentToStream(Stream outputStream, HttpContent content, TransportContext transportContext)
{
//here set the size of buffer
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
//here we re using stream to read file from db server
using (var fileStream = IOC.Container.Resolve<IMongoCommonService>().GridRecordFiles.GetFileAsStream(_fileName))
{
int totalSize = (int)fileStream.Length;
/*here we are saying read bytes from file as long as total size of file
is greater then 0*/
_response.Content.Headers.Add("Content-Length", fileStream.Length.ToString());
// _response.Content.Headers.Add("Content-Range", "bytes 0-"+ totalSize.ToString()+"/"+ fileStream.Length);
while (totalSize > 0)
{
int count = totalSize > bufferSize ? bufferSize : totalSize;
//here we are reading the buffer from orginal file
int sizeOfReadedBuffer = fileStream.Read(buffer, 0, count);
//here we are writing the readed buffer to output//
await outputStream.WriteAsync(buffer, 0, sizeOfReadedBuffer);
//and finally after writing to output stream decrementing it to total size of file.
totalSize -= sizeOfReadedBuffer;
}
}
}
After I load page video start to play immediately, but I can not seek for previous (already played) seconds of video or rewind it as well in Google Chrome browser. When I try to do this, video goes back to the beggining.
But in Firefox and Edge it's working like it should be, I can go back to already played part. I don't know how to solve this issue in Google Chrome Browser
You should use HTTP partial content. As it described here:
As it turns out, looping (or any sort of seeking, for that matter) in elements on Chrome only works if the video file was served up by a server that understands partial content requests.
So there are some articles that may help you to implement it. Try these links:
HTTP 206 Partial Content In ASP.NET Web API - Video File Streaming
How to work with HTTP Range Headers in WebAPI
Here is an implementation of responding to Range requests correctly - it reads a video from a file and returns it to the browser as a stream, so it doesnt eat up your server's ram. You get the chance to decide the security you want to apply etc in code.
[HttpGet]
public HttpResponseMessage Video(string id)
{
bool rangeMode = false;
int startByte = 0;
if (Request.Headers.Range != null)
if (Request.Headers.Range.Ranges.Any())
{
rangeMode = true;
var range = Request.Headers.Range.Ranges.First();
startByte = Convert.ToInt32(range.From ?? 0);
}
var stream = new FileStream(/* FILE NAME - convert id to file somehow */, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) {Position = startByte};
if (rangeMode)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.PartialContent)
{
Content = new ByteRangeStreamContent(stream, Request.Headers.Range, MediaTypeHeaderValue.Parse(fileDetails.MimeType))
};
response.Headers.AcceptRanges.Add("bytes");
return response;
}
else
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(stream)
};
response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(fileDetails.MimeType);
return response;
}
}
I am experiencing some strange behaviour from my code which i am using to stream files to my clients.
I have a mssql server which acts as a filestore, with files that is accessed via an UNC path.
On my webserver i have some .net code running that handles streaming the files (in this case pictures and thumbnails) to my clients.
My code works, but i am experiencing a constant delay of ~12 sec on the initial file request. When i have made the initial request it is as the server wakes up and suddenly becomes responsive only to fall back to the same behaviour some time after.
At first i thought it was my code, but from what i can see on the server activity log there is no ressource intensive code going on. My theory is that at each call to the server the path must first be mounted and that is what causes the delay. It will then unmount some time after and will have to remount.
For reference i am posting my code (maybe i just cannot see the problem):
public async static Task StreamFileAsync(HttpContext context, FileInfo fileInfo)
{
//This controls how many bytes to read at a time and send to the client
int bytesToRead = 512 * 1024; // 512KB
// Buffer to read bytes in chunk size specified above
byte[] buffer = new Byte[bytesToRead];
// Clear the current response content/headers
context.Response.Clear();
context.Response.ClearHeaders();
//Indicate the type of data being sent
context.Response.ContentType = FileTools.GetMimeType(fileInfo.Extension);
//Name the file
context.Response.AddHeader("Content-Disposition", "filename=\"" + fileInfo.Name + "\"");
context.Response.AddHeader("Content-Length", fileInfo.Length.ToString());
// Open the file
using (var stream = fileInfo.OpenRead())
{
// The number of bytes read
int length;
do
{
// Verify that the client is connected
if (context.Response.IsClientConnected)
{
// Read data into the buffer
length = await stream.ReadAsync(buffer, 0, bytesToRead);
// and write it out to the response's output stream
await context.Response.OutputStream.WriteAsync(buffer, 0, length);
try
{
// Flush the data
context.Response.Flush();
}
catch (HttpException)
{
// Cancel the download if a HttpException happens
// (ie. the client has disconnected by we tried to send some data)
length = -1;
}
//Clear the buffer
buffer = new Byte[bytesToRead];
}
else
{
// Cancel the download if client has disconnected
length = -1;
}
} while (length > 0); //Repeat until no data is read
}
// Tell the response not to send any more content to the client
context.Response.SuppressContent = true;
// Tell the application to skip to the EndRequest event in the HTTP pipeline
context.ApplicationInstance.CompleteRequest();
}
If anyone could shed some light over this problem i would be very grateful!
I'm need to implement next page:
User click on button Upload file, select file and uploading it on server. After uploading file will be processed by converter. Converter can return percentage of conversion. How to implement continuous progress bar on page (progress = upload progress + convert progress)?
I'm using PlUpload - this tool can return percentage of uploading file to server, but I can't override returning percentage.
That my upload action:
public ActionResult ConferenceAttachment(int? chunk, string name, Identity cid)
{
var fileUpload = Request.Files[0];
var tempfolder = Path.GetTempFileName().Replace('.', '-');
Directory.CreateDirectory(tempfolder);
var fn = Path.Combine(tempfolder, name);
chunk = chunk ?? 0;
using (var fs = new FileStream(fn, chunk == 0 ? FileMode.Create : FileMode.Append))
{
var buffer = new byte[fileUpload.InputStream.Length];
fileUpload.InputStream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
// CONVERTING ....
return Content("OK", "text/plain");
}
Which architecture solution can solve my problem? Or which JS upload library?
You must use some kind of Real-Time connection or a Streaming to do this.
1) Using SignalR you can create a Hub to notificate the client about the progress, easy to implement and powerfull.
Learn About ASP.NET SignalR
2) You can adapte this example about PushStreamContent to send the progress while you are converting:
Asynchronously streaming video with ASP.NET Web API
You can set buffer size as much as you want. But, I would say to upload 8kb data of file at a time & meanwhile start streaming conversion process asynchronously. But, for smooth progress again what kind of file you wanna upload?
So due to content being on a different drive than my server application's virtual directory, I've had to implement a file getting service.
I am able to the get the file size. Then I want to set the content headers so that the browser knows the total size and thus knows how to proportion the seek bar.
Here I set the total size header:
string bytes = Convert.ToString( fInfo.Length );
Response.AddHeader("Content-Length", bytes );
Response.AddHeader("Content-Range", "bytes 0-" + bytes + "/" + bytes);
Resulting in:
Content-Length: 1389363
Content-Type: video/ogg
Content-Range: bytes 0-1389363/1389364
I downloaded the resulting file and confirmed that the bytes match. Works fine in Firefox, all wacky in chrome. In chrome it plays to the end, but does not move the seek bar and then shows negative infinity in the current time when it reaches the end. ALSO I cannot scrub the video at all, presumably because it has an invalid duration.
playing the same file directly in chrome works properly, so maybe it's some content header that chrome wants that firefox don't give a s#%t about?
Any ideas? Wrong length unit? built-in server side g-zip encoding interfering?
I'm using standard video object:
<video class="videoplayer" controls="" autoplay="autoplay" tabindex="0">
<source type="video/ogg" src="http://vb_html.dev/GetFile.aspx?filename=c:/_assets/4c2c09c2-f2ff-e011-a992-009056af18ff/softsignage/softsignage-00000000600.ogv"></source>
Your browser does not support the video tag.
</video>
For more reference this is how I'm passing the file data:
FileStream mystream = new FileStream(anyFilePath, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[4096];
using (BinaryReader reader = new BinaryReader(mystream))
{
while (true)
{
int bytesRead = mystream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
Response.OutputStream.Write(buffer, 0, bytesRead);
}
}
Try setting status code to 206:
response.StatusCode = 206;
Basically, you want to get the request range in bytes, and return that portion of the file. You can do this in MVC with
RangeFileStreamResult(Stream fileStream, string contentType, string fileName, DateTime modificationDate);
Using the Codeplex example found in this library http://mvcresumingactions.codeplex.com/
--
I am using valums uploader with an ashx file example that I found & tweeked, and want to save data from each file to the db when I upload, stepping through the code , there doesn't seem any way to differentiate which file is uploaded in which order , watching certain variable , the code jumps all over the place. It works fine for single files but more than 1 is an issue. Also, context.Request.Files.Count is always 0
byte[] buffer = new byte[request.ContentLength];
fileName = request.RawUrl.Split('=')[1];
fmt = Path.GetExtension(fileName);
using (BinaryReader br = new BinaryReader(request.InputStream))
br.Read(buffer, 0, buffer.Length);
File.WriteAllBytes(StorageRoot + request["qqfile"], buffer);
success = false;
context.Response.Write("{success:true}");
HttpContext.Current.ApplicationInstance.CompleteRequest();
Any ideas?
I've used valums uploader without problem. Different browsers upload files in different ways. IE will use an file upload html control, whilst Firefox & Chrome can do it Ajax style. I wrote my own upload handler, but the first thing you need to do is figure which method was used.
I do this like this:
if (HttpContext.Current.Request.QueryString["qqfile"] != null)
file = new UploadedFileXhr();
else if (HttpContext.Current.Request.Files.Count > 0)
file = new UploadedFileForm();
else
return new UploadResult("Request was not valid.");
An XHR upload means you read from the input buffer like so:
int read = HttpContext.Current.Request.InputStream.Read(buffer, 0, buffer.Length);
while (read == buffer.Length)
{
ms.Write(buffer, 0, buffer.Length);
read = HttpContext.Current.Request.InputStream.Read(buffer, 0, buffer.Length);
}
ms.Write(buffer, 0, read);
A file upload is simpler like so:
UploadResult result = new UploadResult(true);
result.Stream = _File.InputStream;
result.Name = System.IO.Path.GetFileName(_File.FileName);
Even if you add multiple files in the client, each file is uploaded individually.