0

I'm trying to download files through a popup window. Most of files download correctly, but some one through this error:

Error in /Controladores/Descarga.ashx?guid=4b5050f6-2f69-41e1-8bd6-1113686e9575 File D:...\FileName.pdf.

An error occurred while communicating with the remote host. The error code is 0x80070057.

at System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.FlushCore(Byte[] status, Byte[] header, Int32 keepConnected, Int32 totalBodySize, Int32 numBodyFragments, IntPtr[] bodyFragments, Int32[] bodyFragmentLengths, Int32 doneWithSession, Int32 finalStatus, Boolean& async)

at System.Web.Hosting.ISAPIWorkerRequest.FlushCachedResponse(Boolean isFinal)

at System.Web.Hosting.ISAPIWorkerRequest.FlushResponse(Boolean finalFlush)

at System.Web.HttpResponse.Flush(Boolean finalFlush, Boolean async)

at System.Web.HttpResponse.Flush()

at DownloadFile.Send(HttpContext context) in D:...\AppCode\DownloadFile.vb:line 65

And this is the code in DownloadFile.vb:

    Public Sub Send(context As HttpContext)
        Dim fileData As Byte()
        Dim bytesRead As Long
        Using fs As New FileStream(PrivatePath, FileMode.Open, FileAccess.Read)
            ReDim fileData(fs.Length)
            bytesRead = fs.Read(fileData, 0, CInt(fs.Length))
            fs.Close()
        End Using
        Try
            With context.Response
                If .IsClientConnected Then
                    .ClearContent()
                    .ClearHeaders()
                    .Buffer = True
                    .ContentType = "application/octet-stream"
                    .AddHeader("Content-Length", bytesRead.ToString())
                    .AddHeader("Content-Disposition", $"attachment;filename={New FileInfo(PrivatePath).Name}")
                    .BinaryWrite(fileData)
                    .Flush() 'This is line 65
                    .Close()
                End If
            End With
        Catch ex As Exception
            MailToAdmin($"Error ...", context.Request.RawUrl)
        End Try

    End Sub

Thank you.

5
  • It looks to me as if you were uploading, not downloading. Commented Nov 17 at 12:47
  • No, i don't. In fact, most files download correctly. Commented Nov 18 at 8:21
  • Oh, I see, this is server code. I incorrectly assumed this was executed on the client. Commented Nov 18 at 12:39
  • Could it be that the filename has spaces in it? If so, you would need $"attachment;filename=""{New FileInfo(PrivatePath).Name}""". Doubled double-quotes make one literal double-quote. Commented Nov 18 at 19:46
  • See my answer - it's unlikely spaces, it is probably that the file is still open, or in use by some other process. And if the path name is to a network drive (non local computer), then the filestream code has to loop, and "wait" and "test" if the read is complete. As a result, I suggest dumping the filestream, and I also suggest using the FileInfo object to get the file length. See my post below for a alternative approach. Commented Nov 19 at 4:40

1 Answer 1

0

About the only suggestion?

In place of doing a binary file read, use the built-in TransmitFile. This command also has the bonus (advantage) of not loading the whole file into memory, and thus it's also easier on the memory that IIS web server uses. It also tends to play "nice" with the page lifecycle. And if you are using a network path name? Then the file steam reader will NOT always finish!!! - and that means that the "length" will also not match the file name.

For local files, you can trust the result. However, for path names to network files? Then you have to loop when using the fs.Read, and ensure that all bytes being read is complete.

So, taking the file length from "file info" object does not necessary have to match the bytes read length. They should match, but not always if the fs.read is not 100% done yet. As noted, if these are network path names and files? Then you need to loop and test the fs object to ensure it's has finished reading.

As noted, I thus suggest to not use FileStream reader.

Hence, this code:

        Dim iFileInfo As New FileInfo(PrivatePath)
        ' get content type based on file extension.
        Dim strConType As String = MimeMapping.GetMimeMapping(PrivatePath)

        With context.Response
            If .IsClientConnected Then
                .Clear()
                .ContentType = strConType
                .AppendHeader("Content-Length", iFileInfo.Length.ToString)
                .AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(PrivatePath))
                .TransmitFile(PrivatePath)
                .End()
            End If
        End With

So, I've been using the above for some years now - and 99% of the files are PDF's, and it been flawless. And those file(s) are some rather ugly long network path names.

So, above eliminates the file stream and the binary read. And as noted, TransmitFile() also plays nice with memory, and does not read the whole file into memory, but pulls from disk as required.

So, try above - either TransmitFile() should help, or taking the file length from "file info" object should fix this.

I thus strong suggest you dump the FileStream object reader here, or change the code to loop and test if the reader is finished. The less moving parts here, the better.

Of course the other common issue for this? Some other software still has the file in question open, and has not released it, or is even still modifying the file before the download is triggered. Or even if some other process is copying the file, and again not 100% complete, then you can also get errors as you noted.

Last FYI:

The MimeMapping.GetMimeMapping() function is only available with .net framework 4.5 or later.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you very much, Albert. I think some time ago I used TransmitFile() but, for any reason, I changed it. So, I'll try your solution.
Albert, your solution works. But there's a problem. Now, throw an error executing Response.End() because before all, I call this: Task.Run(Sub() RegisterDownload(_path, (New User).Number, context.Request.UserHostAddress, context.Request.RawUrl)). Thread was being aborted.
Don't you think that you starting a whole new process thread is a Super Big, Huge, large, and massive Mount Everest of a detail? You can't really use and start another thread, since your passing the http context to that thread, and that http context WILL FOR SURE go out of scope after the post-back and page life cycle completes. Once the post back completes, then on server side, the page class (code behind) is disposed of, destroyed, and goes out of scope. And http context ALSO goes out of scope. You gain nothing by attempting to start another process/thread for the download.
After a post-back, the page can either: send back the web page, OR it can send a file - you cannot do both, and thus it makes zero sense to try and move the download over to another process thread, since even if you did do this, and the user hit a button on the page? Then the current http context and instance of the page class and variables will be instant destroyed, disposed of, removed from memory, and then the page life cycle starts over again. So, really, you can't gain or achieve anything of value by trying to start another thread - the http context will go out of scope....
So, you could leave out the Response.End, but this will still not work correctly, since as noted, the http context have a very limited lifespan - only exits when the page is posted back to the server. If while the code behind is running, and not yet finished, and user hits another button on the page? The current page class, and page lifecycle is destroyed - including the http context - it will go out of scope. So, you not going to get (or gain) multiple file(s) downloading by doing this. And if you start another process thread, then that lets current page context go out of scope.
Thank you so much for your detailed explanation. I'll review everything.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.