2

前言

近期在生产项目中遇到了文件上传与下载的问题,业务逻辑如下:

  • Angular从Java服务器中获取Excel模板,供用户下载
  • 用户填写完成后,通过Angular上传至服务器。

本文专注于讨论前后端分离项目的文件下载的问题。

下载(Spring->Angular)

用户从服务器上获取Excel模板

文件传输格式

  • 模板文件放在Spring项目的Resources文件夹中
  • Spring使用File类型来发送文件
  • Angular使用Blob类型来接收文件

放置文件

在Recourses目录放置待传输的文件,类似这样:

image.png
示例文件 /upload/教师上传模板.xlsx

Spring Service

此处有一个坑,在项目的其它代码中发现了上传文件使用的是MultipartFile类型,理所当然的任务下载文件也是MultipartFile类型。
然后怎么使也不行,浪费了好多时间。
最后才知道MultipartFileFile的继承树完全不同,虽然有办法相互转化,但根本不通用。

UserService:

/**
 * 用户模板下载
 *
 * @return 模板文件
 */
File download();

UserServiceImpl:

/**  
 * 用户模板下载
 * 
 * @return File类型 模板文件  
 */
public File download() {
    // throw IOException 需要捕获
    try {
        // 获取文件
        File file = new ClassPathResource("upload/教师上传模板.xlsx").getFile();
        // 返回文件
        return (file);
    } catch (Exception e) {
        // 打印信息,返回null
        logger.debug("模板下载失败");
        return null;
    }
}

需要注意的是 ClassPathResource("upload/教师上传模板.xlsx").getFile(); 中的路径以Resources为根目录。

Spring Controller

UserCortroller:

@GetMapping("download")
public File downloadTemplate() {
    return this.userService.download();
}

至此后端编写完毕,提供接口 user/download

Angular Service

向服务器发起请求。
UserService:

  /**
   * 下载上传模板
   */
  public downloadUploadTemplate(): Observable<Blob> {
    // const url = '/assets/files/userUploadTemplate.xlsx';
    const url = 'api/user/download';
    const httpHeader = new HttpHeaders().append(YunzhiInterceptor.DONT_INTERCEPT_HEADER_KEY, 'true');
    return this.httpClient.get<Blob>(url, {headers: httpHeader, responseType: 'blob' as 'json'});
  }

Angular Component

调用Service方法,然后通过FileSaver调用浏览器API保存文件。

UserComponent:

// 仅提供了功能代码,可以拼接到具体的方法中使用
this.userService.downloadUploadTemplate()
  .subscribe(data => {
    // 获取文件后由FileSaver保存文件
    FileSaver.saveAs(data, '教师上传模板.xlsx');
  }, () => {
    console.log('模板下载失败');
  });

在Chrome中效果展示

image.png

总结

整个过程很容易理解,单独写前端或单独写后端都不难。
重点在于搞清楚通过HTTP传输的这个内容,具体是什么类型。

在Angular内置类中,Blob的注释这样写到:

A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system.

翻译过来就是:

不可变原始数据的类似文件的对象。blob表示不一定是JavaScript原生格式的数据。文件接口基于Blob,继承Blob功能并将其扩展以支持用户系统上的文件。

因此Angular的Blob可以作为后端Flie类型的接收类型。

版权声明

本文作者:河北工业大学梦云智开发团队 - 刘宇轩
新人经验不足,有建议欢迎交流,有错误欢迎轻喷

LYX6666
1.6k 声望73 粉丝

一个正在茁壮成长的零基础小白