编程随笔 编程随笔
  • 前端
  • 后端
  • 嵌入式
  • 星球项目
  • 开源项目
  • 海康AGV
  • 四向车
  • 工具类
  • 项目仓库

    • 部署仓库 (opens new window)
    • 代码仓库 (opens new window)
  • vuepress插件

    • 自动生成导航栏与侧边栏 (opens new window)
    • 评论系统 (opens new window)
    • 全文搜索 (opens new window)
    • 选项卡 (opens new window)
    • 自动生成sitemap (opens new window)
  • 自主开发插件

    • 批量操作frontmatter (opens new window)
    • 链接美化 (opens new window)
    • 折叠代码块 (opens new window)
    • 复制代码块 (opens new window)

liyao52033

走运时,要想到倒霉,不要得意得过了头;倒霉时,要想到走运,不必垂头丧气。心态始终保持平衡,情绪始终保持稳定,此亦长寿之道
  • 前端
  • 后端
  • 嵌入式
  • 星球项目
  • 开源项目
  • 海康AGV
  • 四向车
  • 工具类
  • 项目仓库

    • 部署仓库 (opens new window)
    • 代码仓库 (opens new window)
  • vuepress插件

    • 自动生成导航栏与侧边栏 (opens new window)
    • 评论系统 (opens new window)
    • 全文搜索 (opens new window)
    • 选项卡 (opens new window)
    • 自动生成sitemap (opens new window)
  • 自主开发插件

    • 批量操作frontmatter (opens new window)
    • 链接美化 (opens new window)
    • 折叠代码块 (opens new window)
    • 复制代码块 (opens new window)
  • springboot

  • 服务器相关

  • 腾讯云cos对象操作

    • 初始化客户端
    • 文件上传
    • 文件下载
      • 获取到文件下载输入流
      • 通过路径链接下载
    • 文件删除
    • NodeJs获取签名
  • 后端
  • 腾讯云cos对象操作
华总
2023-10-03
0
0
目录

文件下载原创

# 获取到文件下载输入流

# 后端

# CosManager.java

 /**
     * 下载对象
     *
     * @param key 唯一键
     * @return 文件的字节数组
     */
    public byte[] getObject(String key)throws CosClientException, IOException {
        
      // 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例,如果没有则创建
        COSClient cosClient = createCOSClient();
      
      //下载文件
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, key);
        COSObjectInputStream cosObjectInput = null;

        try {
            COSObject cosObject = cosClient.getObject(getObjectRequest);
            cosObjectInput = cosObject.getObjectContent();
        } catch (CosServiceException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "下载失败");
        }


        // 处理下载到的流
        // 这里是直接读取,按实际情况来处理
        byte[] bytes = null;
        try {
            bytes = IOUtils.toByteArray(cosObjectInput);

        } catch (IOException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "数据读取失败");
        } finally {
            // 用完流之后一定要调用 close()
            cosObjectInput.close();
        }

        // 在流没有处理完之前,不能关闭 cosClient
        // 确认本进程不再使用 cosClient 实例之后,关闭即可
        cosClient.shutdown();
        return bytes;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# CosController.java

/**
     * 文件下载
     *
     * @param uploadFileRequest 文件对象
     * @param request           请求
     * @return 用户id
     */
    @Operation(summary = "文件下载")
    @PostMapping("/download")
    public BaseResponse<byte[]> download(@RequestBody UploadFileRequest uploadFileRequest, HttpServletRequest request) {

        String filepath = uploadFileRequest.getFilepath();
        userService.getLoginUser(request);
        String key = filepath.replace(HOST, "");
        String fileName = StringUtils.substringAfterLast(key, "/");
        String fileExtension = StringUtils.substringAfter(fileName, ".");

        try {

            boolean objectExists = cosClient.doesObjectExist(bucket, key);
            if (!objectExists) {
                // 如果对象不存在,返回相应的响应
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "对象不存在");
            }

            // 调用cosManager.download()下载文件
            byte[] data = cosManager.getObject(key);

            // 推断MIME类型
            MediaType mediaType;
            switch (fileExtension.toLowerCase()) {
                case "pdf":
                    mediaType = MediaType.APPLICATION_PDF;
                    break;
                case "jpg":
                    mediaType = MediaType.IMAGE_JPEG;
                    break;
                // 添加更多文件类型和对应的MediaType
                default:
                    mediaType = MediaType.APPLICATION_OCTET_STREAM;
                    break;
            }

            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(mediaType);
            headers.setContentDispositionFormData("attachment", fileName);

            return ResultUtils.success(data, fileName, mediaType.getType());

        } catch (Exception e) {
            log.error("file download error, filepath = " + filepath, e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "下载失败:"+ e.getMessage());
        }

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 前端

基于vue3 + ts + elementplus

# 接口

 /**
     * 文件下载
     * @param requestBody 
     * @returns BaseResponseByte<any> OK
     * @throws ApiError
     */
    public static download(
requestBody: UploadFileRequest,
): CancelablePromise<BaseResponseByte> {
        return __request(OpenAPI, {
            method: 'POST',
            url: '/cos/download',
            body: requestBody,
            mediaType: 'application/json',
        });
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 使用

html

<el-button type="primary" @click="downloadFile">下载</el-button>
1

ts

function base64toFile(base64Data:any, filename:any, mimeType:any) {
  // 创建一个Base64字符串转换为Uint8Array的函数
  function base64ToArrayBuffer(base64:any) {
	const binaryString = window.atob(base64);
	const length = binaryString.length;
	const bytes = new Uint8Array(length);
	
	for (let i = 0; i < length; i++) {
	  bytes[i] = binaryString.charCodeAt(i);
	}
	
	return bytes;
  }
  
  // 将Base64数据转换为Uint8Array
  const uint8Array = base64ToArrayBuffer(base64Data);
  
  // 创建Blob对象
  const blob = new Blob([uint8Array], { type: mimeType });
  
  // 创建一个URL对象,用于文件下载
  const url = window.URL.createObjectURL(blob);
  
  // 创建一个链接元素并触发下载
  const a = document.createElement("a");
  a.href = url;
  a.download = filename || "file"; // 可以指定文件名,如果未提供默认为"file"
  document.body.appendChild(a);
  a.click();
  
  // 释放URL对象
  window.URL.revokeObjectURL(url);
}


function downloadFile({ $index, row }) {
  return new Promise((resolve, reject) => {
	CosControllerService.download({
	  filepath: row.key
	}).then(res => {
	  base64toFile(res.data, res.key, res.mimeType);
	 resolve(res.data)
	}).catch(error => {
	  reject(error)
	})
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 通过路径链接下载

# 后端

官方文档介绍了 2 种文件下载方式。一种是直接下载 COS 的文件到后端服务器(适合服务器端处理文件),另一种是获取到文件下载输入流(适合返回给前端用户)。

参考官方文档:

  1. https://cloud.tencent.com/document/product/436/65937
  2. https://cloud.tencent.com/document/product/436/10199#.E4.B8.8B.E8.BD.BD.E5.AF.B9.E8.B1.A1

其实还有第三种“下载方式”,直接通过路径链接访问,适用于单一的、可以被用户公开访问的资源,比如用户头像、本项目中的代码生成器图片。

但是对于本项目中的代码生成器产物包文件,更建议通过后端服务器从 COS 下载文件并返回给前端,这样可以在后端限制只有登录用户才能下载。

# CosManager

在 CosManager 中新增对象下载方法,根据对象的 key 获取存储信息:

/**
 * 下载对象
 *
 * @param key 唯一键
 * @return
 */
public COSObject getObject(String key) {
    GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);
    return cosClient.getObject(getObjectRequest);
}
1
2
3
4
5
6
7
8
9
10

# CosController

核心流程是根据路径获取到 COS 文件对象,然后将文件对象转换为文件流,并写入到 Servlet 的 Response 对象中。注意要设置文件下载专属的响应头。

同上,测试接口一定要加上管理员权限!防止任何用户随意上传文件。

测试文件下载接口代码如下:

/**
 * 测试文件下载
 *
 * @param filepath
 * @param response
 * @return
 */
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
@GetMapping("/test/download/")
public void testDownloadFile(String filepath, HttpServletResponse response) throws IOException {
    COSObjectInputStream cosObjectInput = null;
    try {
        COSObject cosObject = cosManager.getObject(filepath);
        cosObjectInput = cosObject.getObjectContent();
        // 处理下载到的流
        byte[] bytes = IOUtils.toByteArray(cosObjectInput);
        // 设置响应头
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + filepath);
        // 写入响应
        response.getOutputStream().write(bytes);
        response.getOutputStream().flush();
    } catch (Exception e) {
        log.error("file download error, filepath = " + filepath, e);
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "下载失败");
    } finally {
        if (cosObjectInput != null) {
            cosObjectInput.close();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 前端

基于react + ts + Ant Design

# 使用 openAPI 工具生成接口

openAPI (opens new window)

# 新增对象存储相关常量。

修改 constants/index.ts 文件,添加下列代码:

/**
 * COS 访问地址
 */
export const COS_HOST = "https://yuzi-1256524210.cos.ap-shanghai.myqcloud.com";
1
2
3
4

# 开发页面

遵循 Flex 左右布局,左边上传文件,右边展示和下载文件。

对于文件上传,直接使用 Ant Design 的拖拽文件上传组件。

官方文档:https://ant.design/components/upload-cn#components-upload-demo-drag

使用 img 标签直接拼接图片地址并展示:

<div>文件地址:{COS_HOST + value}</div>
<Divider />
<img src={COS_HOST + value} height={280} />
1
2
3

使用 file-saver 库,可以下载后端返回的 blob 内容为文件。

先安装 file-saver 库:

npm install file-saver
npm i --save-dev @types/file-saver
1
2

完整下载文件代码如下:

<div>文件地址:{COS_HOST + value}</div>
<Divider />
<img src={COS_HOST + value} height={280} />
<Divider />
<Button
  onClick={async () => {
    const blob = await testDownloadFileUsingGet(
      {
        filepath: value,
      },
      {
        responseType: 'blob',
      },
    );
    // 使用 file-saver 来保存文件
    const fullPath = COS_HOST + value;
    saveAs(blob, fullPath.substring(fullPath.lastIndexOf('/') + 1));
  }}
>
  点击下载文件
</Button>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

由于后端下载文件接口不返回 code 状态码,所以需要修改响应拦截器,对于文件下载请求,直接返回 blob 对象。修改 requestConfig.ts 的部分代码如下:

// 响应拦截器
responseInterceptors: [
  (response) => {
    // 请求地址
    const requestPath: string = response.config.url ?? '';

    // 响应
    const { data } = response as unknown as ResponseStructure;
    if (!data) {
      throw new Error('服务异常');
    }

    // 文件下载时,直接返回
    if (requestPath.includes("download")) {
      return response;
    }

  	...
  },
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#后端#COS
上次更新: 2025/02/18 14:46:10
文件上传
文件删除

← 文件上传 文件删除→

最近更新
01
jFlash使用 原创
03-24
02
中央仓库上传指南 原创
03-23
03
模板生成工具 原创
02-18
04
RTC实时时钟 原创
02-12
05
keil调试 原创
01-21
更多文章>
Copyright © 2023-2025 liyao52033  All Rights Reserved
备案号:鄂ICP备2023023964号-1    
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式