开发者

Sending CSV created on the fly back to client for download

I'm converting a bunch of FOXPRO / FOXWEB apps to ASP.NET.

The underlying DB is still foxpro (for the moment).

I am passing a table to some VB.NET code that I want to have converted to a CSV file and sent back to the client for download. And it works! Sort of ... It works sometimes, but at other times, instead of asking me if I want to download the CSV file, it just spews the file to the browser window.

On the asp side, I am passing the response object, the table and the csv file name.

<%
   Dim xls_fn As String = "test01.csv"

   'OLEDB call to fill up 'tbl' ... this works.

   sendTableAsCSVtoClient(response, tbl, xls_fn)
%>

In the file clsCommon.vb, I have the following code:

Option Explicit On 
'Option Strict On

Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.Page
Imports System.IO
Imports Microsoft.VisualBasic
Imports System.Diagnostics
Imports System.Data
Imports System.Data.OleDb


Public Class clsCommon
    Inherits Page

    Public Shared Function enq(ByVal str As String) As String
        Dim dq As String
        dq = """"
        Return dq & str & dq
    End Function

    ' some other functions and subs defined in here ... blah blah blah
    ' ...

    Public Shared Function sendTableAsCSVtoClient(ByVal resp As HttpResponse, ByVal sqlTable As DataTable, ByVal xls_fn As String) As Boolean
        Dim r As DataRow
        Dim c As DataColumn
        Dim sep As String = ","
        Dim FileExtension As String
        Dim lcFileNameONLY As String
        Dim i As Integer
        Dim dq As String = """"

        FileExtension = UCase(Path.GetExtension(xls_fn))
        lcFileNameONLY = UCase(Path.GetFileNameWithoutExtension(xls_fn))

        resp.Clear()
        resp.ClearContent()
        resp.ClearHeaders()
        resp.ContentType = "application/vnd.ms-excel"
        resp.AddHeader("Content-Disposition", "inline; filename=" & lcFileNameONLY & ".csv")
        For Each c In sqlTable.Columns
            resp.Write(UCase(c.ColumnName) & sep)
        Next
        resp.Write(vbCrLf)

        For Each r In sqlTable.Rows
            For i = 0 To sqlTable.Columns.Count - 1
                resp.Write(enq(r(i)) & sep)
            Next
            resp.Write(vbCrLf)
        Next

        resp.End()
        Return True

    End Function

End Class
  1. What's causing this?
  2. How do I get around it?

I'm guessing it doesn't really matter that the source of the data is a table. Note that the file is created on the fly and never exists on the file sys开发者_如何学运维tem of the server.

tx, tff


Instead of using a Content-Disposition header that is Inline, use Attachment - this will always prompt for a download.

Change the following line from:

resp.AddHeader("Content-Disposition", "inline; filename=" & lcFileNameONLY & ".csv")

To

resp.AddHeader("Content-Disposition", "attachment; filename=" & lcFileNameONLY & ".csv")

See this and this for examples.

The inline type means that the browser is free to render it inline (within the browser), if in knows how to.

And see this SO question, asking why inline sometimes prompts for downloads (the exact opposite of your question...).


The issue is your Content-disposition header. It should be "attachment" instead of "inline".

You may also want to set the content type to be "text/csv"instead of "application/vnd.ms-excel". This way you're more accurate, and if they prefer to use something else for CSV it should work better. However for an in-house app perhaps vnd.ms-excel might work better?


I agree with the Oded and chmullig that you should change the Content-Disposition, but I also recommend using the buffer and finishing the response with a flush:

 resp.Clear()
 resp.Buffer = true

 'build csv

 resp.Flush()
 resp.Close()

I believe a call to .End() throws a ThreadAbortException to stop execution which can cause issues depending on how you handle your exceptions. See here for more info

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜