【问题标题】:How to get the name of a file downloaded with Angular $http?如何获取使用 Angular $http 下载的文件的名称?
【发布时间】:2016-01-07 21:55:51
【问题描述】:

我编写了使用 Angular $http 下载文件的代码。文件名没有在 URL 中指定。 URL 包含文件的唯一标识符,该标识符是从应用程序外部获取的。

$http.get(myUrl) 被调用时,一切正常;该文件被检索,我可以在我的回调处理程序中访问它,但我看不到如何获取文件的名称。使用 Fiddler 捕获原始响应,我看到了:

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 54
Content-Type: application/octet-stream
Server: Microsoft-IIS/8.5
Access-Control-Allow-Origin: http://www.example.com/getFile/12345
Access-Control-Allow-Credentials: true
Content-Disposition: attachment; filename=testfile.txt
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 09 Oct 2015 20:25:49 GMT

Lorem ipsum dolar sit amet!  The contents of my file!

从上面可以清楚地看出,服务器正在“Content-Disposition”中发回文件的名称,但我还没有找到在我的 Angular 回调中访问它。如何从标题中获取文件名?

根据以下答案进行编辑: 我之前应该提到我已经尝试过response.headers()。它返回Object {content-type: "application/octet-stream", cache-control: "private"},所以由于某种原因我仍然没有得到Content-Disposition。 response.headers('Content-Disposition') 返回null

【问题讨论】:

  • 如果response.headers('Content-Disposition') returns null 显然有问题。 @andrew 的答案很完美
  • 您找到解决方案了吗?我有完全相同的问题:Fiddler 清楚地显示了正确的内容处置标头,但以下解决方案均无效: response.headers('Content-Disposition') 返回 null
  • 我能够让它在我正在执行的特定任务的上下文中工作,但我没有一个好的通用答案。
  • 如果 response.headers('Content-Disposition') returns null 并且您在 API 中使用 CORS,则需要确保在响应中添加此标头:access-control-expose-headers:content-dispositionstackoverflow.com/a/44504121/2069306
  • Response.Headers.Add(HeaderNames.AccessControlExposeHeaders, "content-disposition"); 添加到请求函数中,它将授予对该特定调用的脚本的读取权限

标签: angularjs http-headers blob


【解决方案1】:

可能值得一提的是,为了从 HTTP 标头中获取文件名,仅提取 Content-Disposition 标头是不够的。 您仍然需要从此标头值中获取filename 属性。

返回的标头值示例:attachment; filename="myFileName.pdf"

下面的函数会提取filename="myFileName.pdf",然后提取"myFileName.pdf",最后去掉多余的引号得到myFileName.pdf

你可以使用下面的sn-p:

  function getFileNameFromHttpResponse(httpResponse) {
      var contentDispositionHeader = httpResponse.headers('Content-Disposition');
      var result = contentDispositionHeader.split(';')[1].trim().split('=')[1];
      return result.replace(/"/g, '');
  }

【讨论】:

  • 您可能需要根据您的 Angular 版本调整 @andrew 提供的函数的第一行。 var contentDispositionHeader = httpResponse.headers.get('Content-Disposition')
【解决方案2】:

Web API:我发现将以下代码行添加到我的 IHttpActionResult 实现的 ExecuteAsync(...) 方法中是有效的('response' 是要返回的 HttpResponseMessage):

response.Content.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");

Angular:然后我能够以如下方式解析 Angular 中的文件名('response' 是来自 $http.get 的已解决承诺):

var contentDisposition = response.headers('Content-Disposition');
var filename = contentDisposition.split(';')[1].split('filename')[1].split('=')[1].trim();

【讨论】:

  • 您仍然需要从带有.replace(/"/g, '') 的文件名中删除额外的"":` let contentDisposition = response.headers('Content-Disposition'); let filename = contentDisposition.split(';')[1].split('filename')[1].split('=')[1].replace(/"/g, '').trim(); `
  • 尽管您的评论可能是正确的,但这是原始问题的解决方案,因此在该上下文中是一个有效的答案!
  • 对于 .NET Core app.Use((ctx, next) => { ctx.Response.Headers.Add("Access-Control-Expose-Headers", "*");
【解决方案3】:

如果您使用 CORS,则需要将“Access-Control-Expose-Headers”添加到服务器端的响应标头中。例如:Access-Control-Expose-Headers: x-filename, x-something-else

【讨论】:

  • 你部分正确。对于 CORS,服务器必须将标头发送为 Access-Control-Expose-Headers: Content-Disposition
【解决方案4】:

与上面的一些答案类似,但我使用基本的 RegEx 来解决它:

let fileName = parseFilenameFromContentDisposition(response.headers('Content-Disposition'));

function parseFilenameFromContentDisposition(contentDisposition) {
    if (!contentDisposition) return null;
    let matches = /filename="(.*?)"/g.exec(contentDisposition);

    return matches && matches.length > 1 ? matches[1] : null;
}

【讨论】:

  • 这实际上优于其他答案,因为其他答案取决于作为处置标头的第二个标记的文件名。如果不是出于某种原因,那么他们会返回错误的数据。
  • 如果文件名后面可能有一些其他属性,请稍作修改:filename=?([^;]*)。您可能还知道 " 字符,那么我建议filename=\"?([^;"]*?)
【解决方案5】:

使用response.headers获取http响应头:

$http.get(myUrl).then(function (response) {
    // extract filename from response.headers('Content-Disposition')
} 

【讨论】:

  • 我已经试过了。我之前应该提到过,所以我将其附加到我的问题中。
  • 确实,您必须使用Access-Control-Expose-Headers: Content-Disposition 公开标题。这假设您可以控制服务器端响应...
  • 我无法弄清楚这一点。我是否需要“预检”响应或包含实际文件的响应中的“Access-Control-Expose-Headers”?
  • 这就是你在 Rails 后端的做法:jaketrent.com/post/expose-http-headers-in-cors
【解决方案6】:

// 服务

downloadFile(params: any): Observable<HttpResponse<any>> {

    const url = `https://yoururl....etc`;


    return this.http.post<HttpResponse<any>>(
      url,
      params,
      {
        responseType: 'blob' as 'json',
        observe: 'response' as 'body'
      })
      .pipe(
        catchError(err => throwError(err))
      );
  }

// 组件

import * as FileSaver from 'file-saver';

... some code

  download(param: any) {
    this.service.downloadFile(param).pipe(
    ).subscribe({
      next: (response: any) => {

        let fileName = 'file';
        const contentDisposition = response.headers.get('Content-Disposition');
        if (contentDisposition) {
          const fileNameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
          const matches = fileNameRegex.exec(contentDisposition);
          if (matches != null && matches[1]) {
            fileName = matches[1].replace(/['"]/g, '');
          }
        }

        const fileContent = response.body;

        FileSaver.saveAs(fileContent, fileName);

      },
      error: (error) => {

        console.log({error});

      }
    });
  }

享受

【讨论】:

    【解决方案7】:

    也许您已经找到解决方案,但如果其他人有此问题,我会发布此答案。

    在 $http 请求的成功回调函数中添加这些参数:

        $http.get(myUrl).success(function (data, status, headers, config) {
            // extract filename from headers('Content-Disposition')
        });
    

    【讨论】:

      【解决方案8】:

      如果response.headers('Content-Disposition') 返回null,请使用response.headers.**get**('Content-Disposition');

      @andrew 的 sn-p 的其余部分现在运行良好。

      【讨论】:

        【解决方案9】:
        success(function(data, status, headers, response,xhr) {
            console.log(headers('Content-Disposition'));
        }
        

        【讨论】:

          【解决方案10】:

          这里还有很多其他好的答案 - 这是我最终得到的最适合我的 ASP.NET Core 3.1 服务器,使用其中的很多作为指南。

          function getFilename() {
              const header = response.headers.get("Content-Disposition");
              if (!header) {
                  return null;
              }
          
              let matches = /filename=\"?([^;"]+)\"?;?/g.exec(header);
          
              return matches && matches.length > 1 ? matches[1] : null;
          }
          

          【讨论】:

            【解决方案11】:

            这里和another thread 中的许多答案解决了 OP 的具体情况,或者更笼统。我相信你应该从content-disposition npm 包中的parse function 开始。但由于我未能使这个包在我的 Angular 12 应用程序中工作(即使尝试类似于this comment),并且这里的其他答案不能满足我的情况,所以我创建了另一个函数。

            我的文件名标志是Tyłe;k Mopka.png,这会产生一个有效的响应头:

            content-disposition: attachment; filename="Ty_ek; Mopka.png"; filename*=UTF-8''Ty%C5%82ek%3B%20Mopka.png
            

            我们在这里:一个非 ISO-8859-1 字符、一个空格、一个分号。最后一个特别有趣,不仅是因为参数拆分,还因为百分比编码(decodeURI不够,我们需要unescape它)。

            解决方案

            export function parseContentDispositionFilename(contentDisposition: string): string {
              const filename = getFilename(contentDisposition);
              if (filename) {
                return unescape(decodeURI(filename));
              }
              else {
                throw new Error('content-disposition filename cannot be empty');
              }
            }
            
            function getFilename(contentDisposition: string): string | undefined {
              const filenames = getFilenameParams(contentDisposition);
            
              if (filenames.filenamestar) {
                // RFC 6266 4.1 filename* -> RFC 5987 3.2.1 ext-value
                return filenames.filenamestar.replace(/^(?<charset>.+)'(?<language>.*)'(?<filename>.+)$/, '$<filename>');
              }
              else if (filenames.filename) {
                // RFC 6266 4.1 filename (possibly quoted)
                return filenames.filename.replace(/^"(?<filename>.+)"$/, '$<filename>');
              }
              else {
                return undefined;
              }
            }
            
            function getFilenameParams(contentDisposition: string): { filenamestar?: string, filename?: string } {
              // Split using ; (if not quoted) and skip the first element since it's `disposition-type`
              const [, ...dispositionParams] = contentDisposition.split(/(?!\B"[^"]*);\s(?![^"]*"\B)/);
              return {
                filenamestar: getParamValue('filename\\*', dispositionParams),
                filename: getParamValue('filename', dispositionParams),
              };
            }
            
            function getParamValue(paramName: string, params: string[]): string | undefined {
              const regex = new RegExp('^\\s*' + paramName + '=(?<paramValue>.+)\\s*$', 'i');
              return params.find(p => p.match(regex)?.groups?.['paramValue']);
            }
            

            用法

            this.http.get(/*...*/).pipe(
              map(response => {
                const contentDisposition = response.headers.get('content-disposition');
                if (!contentDisposition) {
                  throw new Error('content-disposition header not found');
                }
            
                const filename = parseContentDispositionFilename(contentDisposition);
            
            /*...*/
            

            【讨论】:

              【解决方案12】:

              (文件在浏览器中以二进制格式保存。文件名在客户端的Network/header/Content-Disposition中,我们需要获取文件名)

              In Server-side code:
              node js code-
                  response.setHeader('Access-Control-Expose-Headers','Content-Disposition');
                  response.download(outputpath,fileName);   
              
              In client-side code:
              1)appComponent.ts file
              import { HttpHeaders } from '@angular/common/http';
              this.reportscomponentservice.getReportsDownload(this.myArr).subscribe((event: any) => {
               var contentDispositionData= event.headers.get('content-disposition');
               let filename = contentDispositionData.split(";")[1].split("=")[1].split('"')[1].trim()
               saveAs(event.body, filename); 
              });
              
              2) service.ts file
              import { HttpClient, HttpResponse } from '@angular/common/http';
              getReportsDownload(myArr): Observable<HttpResponse<Blob>> {
               console.log('Service Page', myArr);
               return this.http.post(PowerSimEndPoints.GET_DOWNLOAD_DATA.PROD, myArr, {
                observe: 'response',
                responseType: 'blob'
               });
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-10-07
                • 1970-01-01
                • 1970-01-01
                • 2011-07-08
                • 2018-05-30
                • 1970-01-01
                • 2022-01-01
                • 1970-01-01
                相关资源
                最近更新 更多