1
DISCLAIMER: I posted the same question in Stackoverflow in English, I will carry the valid answer from here to there or from there to here if necessary, but I know that not all users here access Stackoverflow in English, so I intended to expand the answer radius :)
Problem
I am trying to solve this problem for days. We have a process of importing records from a file csv to 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 changing the internal logic, which has already given a significant gain in performance.
However, if I upload large files (well, relatively large, of at most 3MB), I have to wait until the upload process is over until I start importing, and I don’t return any progress information to the client while I do so. The process itself is not so time-consuming, it takes 5 to 10 seconds to complete, and yes, I’ve thought about creating a Task
separate and use Polling to drip the server, but I found this a cannon to kill mosquito.
What I’ve done so far?
So, to fix this problem, I decided to read the stream request and import values while doing so. I created a Handler generic (.ashx), and I put the following code inside the method void ProcessRequest(HttpContext context)
:
using (var stream = context.Request.GetBufferlessInputStream())
{
}
First I remove the headers (request headers), and then I read the stream (through a StreamReader
) until I find a CRLF ( r n), convert the line to my model, and keep reading the CSV. When I get 200 files (or until the end of the file), I update them all at once in the database. Then, I continue the process getting more records until the end of the file.
That seems to work, but then I decided to respond via stream also. First, I disabled the Bufferoutput:
context.Response.BufferOutput = false;
And then, I added these headers to my reply (sponse)
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 base, I write on sponse:
response.Write(s);
response.Flush();
s
is a fixed size string of 256 chars. I know 256 chars don’t always equal 256 bytes, but I just wanted to make sure I didn’t write text walls at once and spoil something.
Here is 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 it here (just the snippet that makes 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 customer’s side, but it still doesn’t seem to work, even when I send a bunch of junk together to go over that buffer. - After a while, after writing a lot on
Response.Write
, the method will become progressively slow until it crashes. The same thing may occur in theResponse.Flush
. I believe I’m letting something slip through here... - I created a simple webform project to test what I’m trying to do. It has a Handler generic 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 on sponse, progress is displayed (the event is called), but ALWAYS after the process is almost over. The problem is that when I get errors, I write these errors in Sponse. That is, the message gets bigger than when everything returns success, because they contain the error messages.
I’m assuming if I write Response.Flush
is not a 100% guarantee that what I wrote will immediately go to the client, right? Or the problem is itself client? If it is the client, why the server hangs when I call the Response.Write
often?
I will be happy to provide further information if you deem it necessary.