2010-02-16 6 views
3

데이터를 Excel 스프레드 시트로 내보내는 데 필요한 여러 페이지가 있습니다. Excel 파일을 생성 할 수는 있지만이 동작을 추상화하는 방법을 연구하여 필요한 모든 페이지에서 쉽게 재사용 할 수 있습니다. 이 코드는 인스턴스 방법으로 잘 작동정적 메서드에서 Response.TransmitFile() 호출

protected void lnkExport_Click(object sender, EventArgs e) 
{ 
    List<List<string>> dataList = GatherDataForSpreadsheet(); 
    Utility.SendExcelFile(this, "fileNameForDownload.xlsx", dataList, "MyReports"); 
} 

: 나는 SendExcelFile를 호출하고있어

public static void SendExcelFile(System.Web.UI.Page callingPage, string downloadFileName, List<List<string>> data, string worksheetTitle) 
{ 
    string tempFileName = Path.GetTempFileName(); 

    try 
    { 
     // Generate file using ExcelPackage 
     GenerateExcelDoc(tempFileName, data, worksheetTitle); 

     callingPage.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName); 
     callingPage.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 
     callingPage.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString()); 
     callingPage.Response.TransmitFile(tempFileName); 
    } 
    finally 
    { 
     //When this is removed, the method works as expected. 
     if (File.Exists(tempFileName)) 
      File.Delete(tempFileName); 
    } 
} 

클릭 처리기는 다음과 같습니다 : 다음과 같이 나의 현재 생각은 정적 유틸리티 메소드를 사용하는 것입니다 호출 페이지의 그러나 정적 방법으로는 전혀 작동하지 않습니다. 이 버튼을 클릭하면 브라우저에 로딩 애니메이션이 무기한 표시되지만 파일 다운로드를 묻는 메시지는 표시되지 않습니다.

저는 ASP.NET (및 웹 프로그래밍 전반)에 매우 익숙하므로 여기에 뭔가 빠졌습니다. 누군가 내가 본 행동을 설명하고이 접근법에 대한 합리적인 대안을 제안 할 수 있습니까?

EDIT : 마지막에 File.Delete() 호출을 제거하면 메서드가 예상대로 작동합니다. Response.TransmitFile()은 전송을 비동기 적으로 수행합니까?

EDIT 2 : 파일을 삭제하기 전에 Response.Flush()를 호출해야했습니다. 아래 내 대답을 참조하십시오. 감사합니다.

public static void SendExcelFile(string downloadFileName, List<List<string>> data, string worksheetTitle) 
{ 
    var context = HttpContext.Current; 
    string tempFolder = context.Request.PhysicalApplicationPath + "temp"; 
    string tempFileName = tempFolder + "tempFileName.xlsx" 

    if (!Directory.Exists(tempFolder)) 
     Directory.CreateDirectory(tempFolder); 

    // Generate file using ExcelPackage 
    GenerateExcelDoc(tempFileName, data, worksheetTitle); 

    context.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName); 
    context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 
    context.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString()); 
    context.Response.TransmitFile(tempFileName); 

    File.Delete(tempFileName); 
} 

또 다른 대안이 방법을 포함 페이지의 기본 클래스, 즉 훨씬 쉽게 경로 일 수 있습니다

+1

코드를 디버그 모드로 이동 했습니까? 그렇다면 시간이 초과되는 부분 (또는이 방법으로 들어가는 경우)을 말할 수 있습니까? 'SendExcelFile' 메쏘드가 예외를 던지고 있습니까? 그렇다면 예외의 스택 추적을 추가하십시오. 그러면이 문제를 해결하는 데 도움이 될 것입니다. 아, 그리고이 메소드에 대한 샘플 호출도 도움이 될 것입니다 (아마도 클릭 핸들러). –

+0

나는 그것을 통해 디버깅했습니다. SendExcelFile 메서드가 정상적으로 실행 된 다음 종료되지만 브라우저에서 파일 다운로드를 묻지 않습니다. IE 또는 Firefox의 탭에는로드 애니메이션이 표시되며 브라우저 하단에 페이지로드 진행률 표시 줄이 있습니다. 페이지 자체는 반응 적입니다. – Odrade

+0

원하는 경우 클릭 처리기를 게시 할 수 있지만 도움이 될지 의심 스럽습니다. 간단히 말해서 다음과 같습니다. var data = GenerateData(); Utility.SendExcelFile (data); – Odrade

답변

5

데이터를 보내기 전에 임시 파일을 삭제하는 것이 문제였습니다. Response.Flush()를 다음과 같이 호출해야합니다.

public static void SendExcelFile(System.Web.UI.Page callingPage, string downloadFileName, List<List<string>> data, string worksheetTitle) 
{ 
    string tempFileName = Path.GetTempFileName(); 

    try 
    { 
     // Generate file using ExcelPackage 
     GenerateExcelDoc(tempFileName, data, worksheetTitle); 

     callingPage.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName); 
     callingPage.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 
     callingPage.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString()); 
     callingPage.Response.TransmitFile(tempFileName); 
     callingPage.Response.Flush(); //This is what I needed 
    } 
    finally 
    { 
     if (File.Exists(tempFileName)) 
      File.Delete(tempFileName); 
    } 
} 
+0

도움을 주신 모든 분들께 감사드립니다. – Odrade

1

이 시도, 당신은이 RequestResponse 직접 HttpContext.Current 떨어져 얻을 수 있습니다. 당신의 페이지는이 같은 뭔가 다른에서 상속 할 수 System.Web.UI.Page에서 상속 할 필요가 없습니다 :

public class BasePage : System.Web.UI.Page 
{ 
    public void SendExcelFile(string downloadFileName, List<List<string>> data, string worksheetTitle) 
    { 
     string tempFolder =Request.PhysicalApplicationPath + "temp"; 
     string tempFileName = tempFolder + "tempFileName.xlsx" 

     if (!Directory.Exists(tempFolder)) 
      Directory.CreateDirectory(tempFolder); 

     // Generate file using ExcelPackage 
     GenerateExcelDoc(tempFileName, data, worksheetTitle); 

     Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName); 
     Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 
     Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString()); 
     Response.TransmitFile(tempFileName); 

     File.Delete(tempFileName); 
    } 
} 

그런 다음 페이지의 클래스는 같이 보인다 :

public partial class MyPage : BasePage 
{ 
    //Stuff! 
} 
+0

접근법 # 1은 내 코드 (응답 대기 시간 초과)와 동일한 결과를 제공합니다. 기본 클래스를 사용하는 것은 쉬운 대안처럼 보입니다.하지만 여전히 접근 # 1이 작동하지 않는 이유를 이해하고 싶습니다. – Odrade

0

나는 이것을 대신 사용할 것이다. 현재 HTTP 컨텍스트는 모든 페이지에서 사용할 수 있습니다.

HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + downloadFileName); 
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 
HttpContext.Current.Response.AddHeader("Content-Length", new FileInfo(tempFileName).Length.ToString()); 
HttpContext.Current.Response.TransmitFile(tempFileName); 
+0

아, HttpContext.Current.Response.Clear();를 사용하십시오. 먼저 – nbushnell

+0

Nick이 제안했지만 작동하지 않는 것 같습니다. 정적 유틸리티 클래스에서 호출하기 때문에 그렇습니까? – Odrade

+0

어떤 부분이 작동하지 않습니까? 컴파일하지 않거나 출력을주지 않는 것입니까? – nbushnell

1

우리는 더 많은 정보가 필요합니다.

난 그냥 클라이언트에 호출 페이지의 복사본을 보내는 제거 다운 버전을 생성하고이를로서 기대 작품 :

public partial class main : System.Web.UI.Page { 
    protected void SendButton_Click(object sender, EventArgs e) {   
     Utility.SendExcelFile(this); 
    } 
} 

당신을 수행

public class Utility { 
    // This just sends the client a copy of the calling page itself 
    public static void SendExcelFile(Page callingPage) { 
     string path = callingPage.Request.PhysicalPath; 
     callingPage.Response.AddHeader("Content-Disposition", "attachment;filename=test.xls"); 
     callingPage.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 
     callingPage.Response.AddHeader("Content-Length", new FileInfo(path).Length.ToString()); 
     callingPage.Response.TransmitFile(path); 
    } 
} 

가 여기 내 호출 페이지의 구현과 다른 점은 무엇입니까?

+0

좋아, 분명히 중요한 부분이 빠져있다. 잠깐 보자. – Odrade

+0

주요 차이점은 File.Delete() 호출입니다. 제거하면 정상적으로 작동합니다. Response.TransmitFile()이 동기가 아니라는 것을 의미합니까? – Odrade

+0

파일을 삭제하기 전에 Response.Flush()를 호출하기 만하면됩니다. 당신의 도움을 주셔서 감사합니다! – Odrade

1

이 시점에서 Fiddler과 같은 HTTP 디버깅 프록시를 사용하면 작업중인 (페이지 코드 숨김) 코드와 비 작업 (정적) 버전에서 생성 된 HTTP 세션을 비교할 수 있습니다.

두 번째 사용자의 임시 파일이 두 번째 사용자의 파일에 의해 덮어 쓰여질 수 있으므로 두 개 이상의 사용자가 동시에 단추를 클릭하면 작성된 코드가 제대로 작동하지 않습니다. 두 번째 사용자의 파일이 전송되는 중에 삭제 될 수 있습니다! 파일 이름에 Path.GetTempFileName() 또는 guid를 사용하여 각 사용자의 파일 이름을 고유하게 지정하십시오.

+0

Mike의 의견에 동의합니다. 아마도 'finally'블록 내의 파일을 삭제할 가치가 있습니다. –

+0

실제로 고유 한 파일 이름을 생성하기 위해 현재 타임 스탬프를 사용하는 논리가 있지만 샘플 코드에서 제거했습니다. Path.GetTempFileName()은 좋은 팁입니다. – Odrade

+0

악의적 인 사용자가 다른 사람의 임시 파일을 다운로드 할 우려가있는 경우에도 유용합니다. 임시 파일이 ~/temp /에 저장되어있는 경우 해당 액세스를 거부하도록 IIS를 구성했는지 확인해야합니다 폴더. –

관련 문제