nest入门记之「实现文件下载接口」

4,255 阅读2分钟

前言

本文介绍如何在Nest实现文件下载以及科普下浏览器下文件打开与文件下载的区别。

实现原理

我的nest项目是默认的基于express实现。

问题等价于express如何实现一个下载接口?

  1. 读取文件,提取文件流
  2. 将文件流pipe置入响应
const writeStream = fs.createReadStream(filePath)
stream.pipe(response)
....略去一系列setHeader操作

这么麻烦?其实express有两个API:res.sendFile和res.download直接封装好上述的操作

回到主题,在nest里应该如何实现呢下载接口?

直接上代码

import { ClassSerializerInterceptor, Controller, Get, Logger, Query, Res, UseGuards, UseInterceptors } from '@nestjs/common';
import { Response } from 'express';
......
  @Get('download')
    async download(@Res() response: Response): Promise<any> {
      ......
            response.download(filePath, (err) => {
                if (!err) {
                    this.logger.log('success', 'download');
                    return;
                }
                this.logger.error(err.stack);
                response.send(ApiResult.Error_Filter(500, '下载失败', err));
            })
    }

这里要注意的点是:Response必须从Express导入的,我们使用的是express底层的能力;并且nest的Respoonse是装饰器。

sendFile VS donwload

两个API有什么区别? 二者都是读取文件流并且以流响应,但download才是真正的下载。

如响应一个txt文本的时候,如果使用sendFile,浏览器会直接显示这段文本。但使用download是可以弹出“另存为”提示保存。

sendFile文档描述

  Transfers the file at the given path.
  Sets the Content-Type response HTTP header field based on the filename’s extension. 
  Unless the root option is set in the options object, path must be an absolute path to the file.

在给定的位置传输文件path。Content-Type根据文件名的扩展名设置响应HTTP标头字段。除非root在options对象中设置了选项,path否则必须是文件的绝对路径。

donwload文档描述

Transfers the file at path as an “attachment”. Typically, browsers will prompt the user for download. 
By default, the Content-Disposition header “filename=” parameter is path (this typically appears in the browser dialog).
Override this default with the filename parameter.

sendFile告知浏览器它是一个流,但没让浏览器下载它。而download在响应头里加上了Content-Disposition: attachment; filename="xxxx" 告知浏览器需要下载。

这是mdn上的介绍

When used in combination with Content-Disposition: attachment, it is used as the default filename for an eventual "Save As" dialog presented to the user.