无法使用angular 6将图像下载为zip文件

时间:2018-11-20 16:52:48

标签: javascript angular typescript

这里我有下面这样的动态数据

data =  [
  "https://dummyimage.com/200x200/000/fff.jpg&text=test", 
  "https://dummyimage.com/200x200/000/fff.jpg&text=testOne",
  "https://dummyimage.com/200x200/000/fff.png&text=testTwo"
]

在按钮上单击我想从这些URL中获取所有图像并将其保存为zip

问题:每当我能够以zip格式下载文件并尝试将其解压缩时,由于无法打开image.zip作为存档,我将收到错误消息;如果我另存为单个图像,则该图像也不会打开并且存在任何方式存储

下面是我的代码

downloadImageData(){


  var blob = new Blob([this.data], { type:  'application/zip'' });

  FileSaver.saveAs(blob,'image.zip');

}

在这里,我同时拥有png和jpg以及各种类型的数据,因此链接获得的所有数据都必须以zip文件格式下载,因此对于角度5+来说,有什么方法可以解决。我也在使用filesave angular package

JS ZIP _body响应

通过使用http模块im获取以下数据[

  {
    "_body": {

    },
    "status": 200,
    "ok": true,
    "statusText": "OK",
    "headers": {
      "date": [
        "Sun",
        " 25 Nov 2018 12:18:47 GMT"
      ],
      "cache-control": [
        "public",
        " max-age=43200"
      ],
      "expires": [
        "Mon",
        " 26 Nov 2018 00:18:47 GMT"
      ],
      "content-disposition": [
        "attachment; filename=2B.JPG"
      ],
      "content-length": [
        "40649"
      ],
      "server": [
        "Werkzeug/0.14.1 Python/2.7.13"
      ],
      "content-type": [
        "image/jpg"
      ]
    },
    "type": 2,
    "url": "http://some url"
  }
]

2 个答案:

答案 0 :(得分:2)

我已经创建了一个演示应用程序here

P.S:该代码仅供参考,可能不包括标准的编码做法,但可以指导您创建自己的解决方案版本。

我正在使用jszip来压缩文件。

app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent }  from './app.component';
import * as JSZip from 'jszip';
import { saveAs } from 'file-saver';

@NgModule({
  imports:      [ BrowserModule, HttpClientModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

app.component.ts:

import { OnInit, Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as JSZip from 'jszip';
import { saveAs } from 'file-saver';

@Component({
  selector: 'my-app',
  template: `<button (click)='downloadZip()'>Download</button>`
})
export class AppComponent {

  constructor(private http: HttpClient) {
  }

  downloadZip() {
    this.loadSvgData("https://c.staticblitz.com/assets/client/icons/file-icons/angular-component-31179578a9a8a16512e9e90ade26549a.svg",
    this.saveAsZip);
  }

  private loadSvgData(url: string, callback: Function) : void{
    this.http.get(url, { responseType: "arraybuffer" })
             .subscribe(x => callback(x));
  }

  private saveAsZip(content: Blob) : void{
    var zip = new JSZip.default();
    zip.file("image.svg", content);
    zip.generateAsync({ type: "blob" })
       .then(blob => saveAs(blob,'image.zip'));
  };
}

说明:

该应用程序只有一个按钮,单击该按钮将使用HttpClient从服务器下载图像文件。它将使用jszip压缩下载的数据,并使用file-saver将其保存到浏览器。

我希望这会有所帮助!

答案 1 :(得分:2)

我来晚了,但是这段代码将使用您的图像数组并创建GET请求。之后,它将执行所有请求,并将响应添加到您的zip文件中,然后下载。

如果您不喜欢使用fileSaver,我提供了两种下载文件的方法。选择您喜欢的任何一个。

编辑:

如果您使用的是旧版本的rxjs,则必须以其他方式导入forkJoin,请查阅rxjs文档。 另外,请确保您的后端允许下载文件,否则会出现CORS错误。

forkJoin Documentation

app.component.ts

import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { forkJoin } from "rxjs";
import { saveAs } from "file-saver";
import * as JSZip from 'jszip';

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {

  data = [
    'http://yoururl/file.png',
    'http://yoururl/file2.png'
  ];

  getRequests = [];

  constructor(private _http: HttpClient) {}

  download() {
    this.createGetRequets(this.data);

    forkJoin(...this.getRequests)
     .subscribe((res) => {
      const zip = new JSZip();

      res.forEach((f, i) => {
        zip.file(`image${i}.png`, f);
      });

      /* With file saver */
      // zip
      //   .generateAsync({ type: 'blob' })
      //   .then(blob => saveAs(blob, 'image.zip'));

      /* Without file saver */
      zip
        .generateAsync({ type: 'blob' })
        .then(blob => {
          const a: any = document.createElement('a');
          document.body.appendChild(a);

          a.style = 'display: none';
          const url = window.URL.createObjectURL(blob);
          a.href = url;
          a.download = 'image.zip';
          a.click();
          window.URL.revokeObjectURL(url);
        });
     });
  }

  private createGetRequets(data: string[]) {
    data.forEach(url => this.getRequests.push(this._http.get(url, { responseType: 'blob' })));
  }
}

app.component.html

<div style="text-align:center">
  <button (click)="download()">Download</button>
</div>

我还必须在tsconfig.json中包含jszip的路径。根据您的Angle版本,您不必执行此操作。在"compilerOptions"内添加以下内容:

tsconfig.json

"paths": {
      "jszip": [
        "node_modules/jszip/dist/jszip.min.js"
      ]
    }

更新:

这是旧的HttpModule的解决方案,我尝试了一下,它可以工作。我建议尽可能更改为新的HttpClientModule。

UPDATE2:

就像我在评论中所说,保存文件以处理不同的文件类型时,可以更改文件扩展名。这是一个示例,您可以轻松扩展此解决方案。

app.component.ts

import { Component } from "@angular/core";
import { Http, ResponseContentType } from "@angular/http"; // Different Import
import { forkJoin } from "rxjs";
import { saveAs } from "file-saver";
import * as JSZip from "jszip";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {

  /* 
    UPDATE 2
    Create a Type map to handle differnet file types 
  */
  readonly MIME_TYPE_MAP = {
    "image/png": "png",
    "image/jpeg": "jpg",
    "image/jpg": "jpg",
    "image/gif": "gif"
  };

  data = [
    "http://url/file.png",
    "http://url/file.jpeg",
    "http://url/file.gif"
  ];

  getRequests = [];

  constructor(private _http: Http) {} // Different Constructor

  download() {
    this.createGetRequets(this.data);

    forkJoin(...this.getRequests).subscribe(res => {
      const zip = new JSZip();
      console.log(res);
      /*
        The return value is different when using the HttpModule.
        Now you need do access the body of the response with ._body,
        as you can see inside the forEach loop => f._body
      */
      let fileExt: String;  // UPDATE 2

      res.forEach((f, i) => {
        fileExt = this.MIME_TYPE_MAP[f._body.type]; // UPDATE 2, retrieve type from the response.
        zip.file(`image${i}.${fileExt}`, f._body);  // UPDATE 2, append the file extension when saving
      });

      zip
        .generateAsync({ type: "blob" })
        .then(blob => saveAs(blob, "image.zip"));
    });
  }

  private createGetRequets(data: string[]) {
    /*
      Change your responseType to ResponseContentType.Blob
    */
    data.forEach(url =>
      this.getRequests.push(
        this._http.get(url, { responseType: ResponseContentType.Blob })
      )
    );
  }
}

UPDATE3:

从URL提取文件名的解决方案,因此不需要文件类型:

import { Component } from "@angular/core";
import { Http, ResponseContentType } from "@angular/http";
import { forkJoin } from "rxjs";
import { saveAs } from "file-saver";
import * as JSZip from "jszip";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  data = ["http://url/file.png", "http://url/file.jpg", "http://url/file.gif"];

  getRequests = [];

  constructor(private _http: Http) {}

  download() {
    this.createGetRequets(this.data);

    forkJoin(...this.getRequests).subscribe(res => {
      const zip = new JSZip();
      let fileName: String;

      res.forEach((f, i) => {
        fileName = f.url.substring(f.url.lastIndexOf("/") + 1); // extract filename from the response
        zip.file(`${fileName}`, f._body); // use it as name, this way we don't need the file type anymore
      });

      zip
        .generateAsync({ type: "blob" })
        .then(blob => saveAs(blob, "image.zip"));
    });
  }

  private createGetRequets(data: string[]) {
    data.forEach(url =>
      this.getRequests.push(
        this._http.get(url, { responseType: ResponseContentType.Blob })
      )
    );
  }
}