下载文件导致用户退出

Posted

技术标签:

【中文标题】下载文件导致用户退出【英文标题】:Downloading file causes user to be logged out 【发布时间】:2020-06-26 10:29:59 【问题描述】:

我有一个 ASP.NET MVC 4.5.2 网站(由于托管限制),其中有一个带有操作链接的管理员限制区域,当用户点击该链接时,该链接会为用户下载 .csv 或 .xlsx 文件。

在本地它工作得很好。

但是,当发布到生产环境时,单击下载按钮会导致用户退出并显示登录屏幕,就好像他们没有经过身份验证一样。然后,登录会真正开始下载(因为 returnUrl 被设置为下载按钮的操作链接)。

以下代码 sn-ps 是我所拥有的修剪版本:

管理员控制器

[Authorize(Roles = "Admin")]
    public class AdminController : BaseController
    
         ...

        [HttpGet]
        public ActionResult Users()
        
            var users = _context.PreSignups
                                .Where(x => x.IsDeleted == false)
                                .OrderBy(x => x.DateCreated).ToList();

            CheckMessages();

            return View(users);
        

        [HttpGet]
        public ActionResult Download_PreSignupUsersToExcel()
        
            var users = _context.PreSignups
                                .Where(x => x.IsDeleted == false)
                                .OrderBy(x => x.DateCreated).ToList().ToDataTable();

            DocumentService.WriteToSpreadsheet(users, $"PreSignupUsers_DateTime.UtcNow.ToString("yyyy_MM_dd_hh_mm_ss").xlsx", true, Response);

            // NOTE: Due to the Response being sent back in DocumentService.WriteToSpreadsheet() the below is mostly redundant as the response has already closed by this point.

            TempData["Message"] = "Download started";

            return RedirectToAction("Logs", "Admin");
        

DocumentService.cs

public static void WriteToSpreadsheet(DataTable dt, string filename, bool hasHeaders, HttpResponseBase response, enFileType fileType = enFileType.Xlsx)
        

            ... // Build the data

            // Write the data
            using (var exportData = new MemoryStream())
            
                response.Clear();
                workbook.Write(exportData);
                if (fileType == enFileType.Xlsx) //xlsx file format
                
                    response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                    response.AddHeader("Content-Disposition", $"attachment;filename=filename");
                    response.BinaryWrite(exportData.ToArray());
                
                else if (fileType == enFileType.Xls)  //xls file format
                
                    response.ContentType = "application/vnd.ms-excel";
                    response.AddHeader("Content-Disposition", $"attachment;filename=filename");
                    response.BinaryWrite(exportData.GetBuffer());
                
                response.End();
            
        

观点 在某些时候包含以下操作链接以对上述内容进行 GET 调用。单击此按钮会导致用户明显以"admin/Download_PreSignupUsersToExcel" 作为returnUrl 注销到登录屏幕。

@html.ActionLink("Export .Xlsx", "Download_PreSignupUsersToExcel", "Admin", null, new  @class="btn btn-primary" )

作为参考,BaseController 继承自 Controller 类,并提供了一种存储 ApplicationDbContext 和 LoggingService 的便捷方式,并且对上述内容没有任何影响。

我已经对代码视而不见了。

有什么想法吗?

【问题讨论】:

【参考方案1】:

好的,事实证明浏览器只会监听特定请求的单个响应。

我将代码的下载部分移到了它自己的 AdminDownloadController API 中。然后通过删除response.End(); 并返回带有所有相关数据的HttpResponseMessage 并用它替换控制器中的标准ActionResult,下载在同一个经过身份验证和授权的会话中启动,而无需注销用户。

需要注意的是,如果没有一些 JS 魔法,您无法对客户端的响应做很多事情,例如显示消息。 AJAX 调用在身份验证和授权方面存在问题,因此请选择阻力最小的路径...除非您遇到这种情况!

以下是适用于任何需要它的人的代码摘要:

AdminDownloadController.cs

[Authorize(Roles = "Admin")]
    public class AdminDownloadController : BaseApiController
    

        // GET: api/AdminDownload/PreSignupUsersToExcel
        public HttpResponseMessage Get(string downloadName)
        
            var response = new HttpResponseMessage(HttpStatusCode.NotFound);

            switch (downloadName)
            
                case "PreSignupUsersToExcel":
                    var users1 = _context.PreSignups
                                .Where(x => x.IsDeleted == false)
                                .OrderByDescending(x => x.DateCreated).ToList().ToDataTable();

                    response = DocumentService.WriteToSpreadsheet(users1, $"PreSignupUsers_DateTime.UtcNow.ToString("yyyy_MM_dd_hh_mm_ss").xlsx", true);
                    break;

                    ...

            

            return response;
         
     

DocumentService.cs

public static HttpResponseMessage WriteToSpreadsheet(DataTable dt, string filename, bool hasHeaders, enFileType fileType = enFileType.Xlsx)
            

    // Build Data
    ...

    // StreamData
    using (var exportData = new MemoryStream())
    
        workbook.Write(exportData);

        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new ByteArrayContent(exportData.ToArray());
        response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
        response.Content.Headers.ContentDisposition.FileName = filename;

        if (fileType == enFileType.Xlsx)
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        else if (fileType == enFileType.Xls)
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");

        return response;
    

WhateverView.cshtml

...

@Html.ActionLink("Export .Xlsx", "AdminDownload", "Api", new  downloadName = "PreSignupUsersToExcel" , new  @class = "btn btn-primary" )

...

【讨论】:

以上是关于下载文件导致用户退出的主要内容,如果未能解决你的问题,请参考以下文章

我记得迅雷有一个DLL文件 把它放在根目录能大大提升下载速度(像是)

配置文件无效会导致用户必须重新下载应用程序

SVG 下载导致文件中包含“无效的 SVG 代码”

Azure SDK for php blob 下载导致内存不足

pikachu--不安全的文件下载

任意文件下载(pikachu)