DISCLAIMER: I asked the same question in StackOverflow in English, I'll carry the valid answer from here to there, if necessary, but I know that not all users here access the StackOverflow in English, so I intended to extend the radius of response:)
Problem
I have been trying to resolve this problem for days. We have a process of importing records from a csv file into the database , through an administrative page that resides in a project ASP.NET Web Forms (.NET 4.0) . The process was slow and I was responsible for making it more agile. I started by altering the internal logic, which already gave a significant gain in performance.
However, if I upload large files (well, relatively large of max. 3 MB), I have to wait until the upload process finishes until I start importing, and I do not return any progress information to the client while I do this. The process itself is not so time-consuming, it takes 5 to 10 seconds to complete, and yes, I've thought about creating a separate Task
and using polling to ping the server, but I found this a cannon to kill mosquito.
What have I done so far?
So, to fix this problem, I decided to read the stream from the request and import the values while doing so. I created a generic handler (.ashx), and put the following code inside the void ProcessRequest(HttpContext context)
:
using (var stream = context.Request.GetBufferlessInputStream())
{
}
First I remove the headers (request headers), and then I read the stream (through StreamReader
) until I find a CRLF r \ n), I convert the line to my model , and continue reading the CSV. When I get 200 files (or to the end of the file), I update all at once in the database. So I continue the process by getting more records to the end of the file.
This seems to work, but then I decided to respond via stream as well. First, I disabled BufferOutput :
context.Response.BufferOutput = false;
And so, I've added these headers to my response )
context.Response.AddHeader("Keep-Alive", "true");
context.Response.AddHeader("Cache-Control", "no-cache");
context.Response.ContentType = "application/X-MyUpdate";
Then, after sending the 200 records to the database, I write in response :
response.Write(s);
response.Flush();
s
is a fixed-length string of 256 chars. I know that 256 chars are not always 256 bytes long, but I just wanted to be sure not to write text walls at once and ruin something.
Here's the format:
| pipeline (delimitador)
1 or 0 sucesso ou falha
; delimitador
mensagem de erro (se houver)
| pipeline (próximo delimitador)
Example:
|0;"Preço inválido na linha 123"|1;|1;|0;"Id inválido na linha 127"|
In the client-side , I have this here (just the snippet that does the request):
function import(){
var formData = new FormData();
var file = $('[data-id=file]')[0].files[0];
formData.append('file', file);
var xhr = new XMLHttpRequest();
var url = "/Site/Update.ashx";
xhr.onprogress = updateProgress;
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Name", file.name);
xhr.setRequestHeader("X-File-Type", file.type);
xhr.send(formData);
}
function updateProgress(evt){
debugger;
}
What happened: (
- The data is not sent immediately to the client when I call
response.Flush
. I understand that there is buffering on the client side, but it still does not seem to work even when I send a bunch of junk together to get past that buffer. - After a while, after typing a lot in%%, the method will progressively slow down until it stops. The same thing can happen in
Response.Write
. I think I'm letting something go through here ... - I've created a simple webform project to test what I'm trying to do. It has a generic handler that will return one number per second for 10 seconds. It actually updates (not always at the rate of 1 second per update) and I can see progress.
- When I write a few lines in the response , the progress is displayed (the event is called), but ALWAYS after the process is almost finished. The problem is that when getting errors, I write these errors in the response. That is, the message gets bigger than when everything returns success, because they contain the error messages.
I'm assuming that if I write Response.Flush
it's not a 100% guarantee that what I wrote goes immediately to the client , right? Or is the problem client itself? If it is the client , why does the server hang when I call Response.Flush
many times?
I'll be happy to provide more information if you feel it is necessary.