Node下载阿里OSS存储文件【不知目录结构】

news2024/9/27 19:15:38

前言:前端传模型ID,后台根据ID去阿里OSS存储下载对应文件(不知文件内部层级结构,且OSS只能单个文件下载),打包成zip字节流形式返回给前端下载。

需求分析:

  • 生成OSS文件关系树
  • Node做文件下载存储
  • 打包返回给前端

一、前期准备

OK! 现在我们做完了需求调研,当然也要确认技术基本架构,因为这个需求是针对模型资源的,后期会继续累加对应需求(上传、拷贝、预览、引用、编辑等),领导要求分模块构建项目,故新构建一个后台项目,如下:

  • dependencies
    • Koa2(body、router、cors) + mysql2(sequelize) + adm-zip + ali-oss
  • devDependencies
    • dotenv + nodemon

image.png

二、完成业务需求

1.生成OSS文件关系树

我们开发是对接的ali-oss开发文档,会调用内置文件操作api,代码中会写明。
代码遵循单一原则

// 获取目录和文件地址
async function getUrl(url, modelId) {
    // modelOSS.listV2, 列举文件层级最深值为100
    return await modelOSS.listV2({
        prefix: url ? url : "models/" + modelId + '/',
        // 设置正斜线(/)为文件夹的分隔符。
        delimiter: "/",
    });
}
// 获取文件夹数组集合 和 文件数组集合
// 思路:
//     判断是否是最后一层,
//     使用递归把每一层的文件夹或文件地址存储到数组
//     运用闭包做数据持久化

async function getPathArr(modelId){
    const prefixesArr = []; // 文件夹数组集合
    const objectsArr = []; // 文件数组集合

    let isFirst = true;

    const resultPaths = async (url) => {
        const resultList = await getUrl(url, modelId); // 获取目录
        if(resultList.prefixes) {
            // 获取文件夹
            resultList.prefixes.forEach(async (subDir) => {
                    prefixesArr.push(subDir);
                    // console.log("SubDir: %s", subDir);
            });
        }

        if(resultList.objects) {
            // 获取文件
            resultList.objects.forEach(async (obj) => {
                    objectsArr.push(obj.name);
                    // console.log("Object: %s", obj.name);
            });
        }

        if(url)	isFirst = false;
    }


    if(isFirst) await resultPaths();

    for(let i = 0; i < prefixesArr.length; i++){
        if(prefixesArr[i].substr(-1) === '/'){
                await resultPaths(prefixesArr[i])
        }
    }

    return {
        prefixesArr,
        objectsArr
    }
}

返回数据如下,每个模型ID返回的对应文件都可能不同

image.png

2.Node做文件下载存储

  • fs中间件不支持逐级创建文件路径,故手写一个公共方法:

    // 通过文件地址,逐级创建文件夹
    function newFolders(folderPath) {
        const arr = folderPath.split('/') // 分割字符串
        let path = ''
        arr.forEach((value, i) => {
            path += value + '/'
            if (!fs.existsSync(path)) {  //判断是否存在该目录
                fs.mkdirSync(path)
            }
        })
    }
    
  • type判断传过来的路径类型(文件或文件夹),分类创建或下载

    // 通过远端模型文件层级生成文件夹,下载文件
    async function downloadModelFile(targeDirList, type){
        try{
            if(type){
                const result = await modelOSS.getStream(targeDirList); // 下载文件
                // 创建文件并且写入
                const writeStream = fs.createWriteStream(`src/examplefile/${targeDirList}`);
                result.stream.pipe(writeStream);
            } else {
                // 查看是否有此目录,如果没有则创建
                if (!fs.existsSync(`src/examplefile/${targeDirList}`)){
                        newFolders(`src/examplefile/${targeDirList}`);
                }
            }
        }catch(e){
            console.log(e)
        }
    }
    
  • 本地与远端文件一致时,打包文件

    async function downFile(targeDirList, type, modelId) {
        // targeDirList => 文件或文件夹远端地址
        // type => 如果传入代表是文件
        // modelId => 模型ID
    
        // 如果是文件夹的话直接创建,是文件的话直接下载
        try {
            for (let i = 0; i < targeDirList.length; i++) {
                downloadModelFile(targeDirList[i], type); // 文件下载
                let loopNum = i + 1;   
                if (loopNum === targeDirList.length) {
                    var zip = new AdmZip();
                    // 压缩文件夹
                    zip.addLocalFolder('src/examplefile/models/' + modelId);
                    zip.writeZip('src/examplefile/models/' + modelId + '.zip');
                }
            }
        } catch (e) {
            console.log(e);
            return e.status;
        }
    }
    

生成如下:
image.png

3.返回给前端

// 抛出模型下载接口
async function exportModel(ctx, next) {
    try {
        const { modelId } = ctx.request.body;
        // 获取文件夹数组集合 和 文件数组集合
        const { prefixesArr, objectsArr } = await getPathArr(modelId);

        // 目录生成
        await downFile(prefixesArr);
        // 文件下载到对应目录中
        await downFile(objectsArr, 'prefixes', modelId);

        // 前端请求返回
        let userfilepath = `src/examplefile/models/${modelId}.zip`;
        let resultData;
        let stat = fs.readFileSync(`src/examplefile/models/${modelId}.zip`);
        if(fs.existsSync(userfilepath)) {
            resultData = stat.toString('base64');
        } else {
            resultData = { code: 500,message: "模型文件不存在"};
        }

        ctx.body = resultData;
    } catch (e) {
        console.log(e);
    }
}

image.png

总结

时间过得总是好快,一转眼就过了午休的时间,一转眼也快走过了三年,这篇文章只是对需求的分析和初步的代码实现,大家看着图一乐就好,今天上午我们新的领导给我们开会了,这里与大家共勉:
1.找重点的事来做。
2.让自己有时间思考。
3.自身为核心。

最后

水平有限,还不能写到尽善尽美,希望大家多多交流,跟春野一同进步!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ClickHouse 相关面试题

文章目录什么是 ClickHouse&#xff0c;它的特点是什么&#xff1f;ClickHouse的数据存储方式是什么&#xff0c;它与传统的行式存储有什么区别&#xff1f;ClickHouse 引擎ClickHouse的数据模型是什么&#xff0c;它与传统的关系型数据库的数据模型有什么区别&#xff1f;Clic…

【TVM 学习资料】TensorIR 快速入门

本篇文章译自英文文档 Blitz Course to TensorIR 作者是 Siyuan Feng。更多 TVM 中文文档可访问→TVM 中文站 TensorIR 是深度学习领域的特定语言&#xff0c;主要有两个作用&#xff1a; 在各种硬件后端转换和优化程序。自动 tensorized 程序优化的抽象。 import tvm from…

kafka(一) 的架构,各概念

Kafka架构 Kafak 总体架构图中包含多个概念&#xff1a; &#xff08;1&#xff09;ZooKeeper&#xff1a;Zookeeper负责保存broker集群元数据&#xff0c;并对控制器进行选举等操作。 &#xff08;2&#xff09;Producer&#xff1a; 生产者负责创建消息&#xff0c;将消息发…

Kafka 概述

文章目录Kafka定义消息队列消息队列应用场景缓冲/消峰 场景解耦 场景异步通信 场景消息队列两种模式点对点模式发布/订阅模式 ***kafka基础架构Kafka定义 消息队列 目前企业中比较常见的消息队列产品主要有 Kafka、ActiveMQ 、RabbitMQ 、RocketMQ 等。在大数据场景主要采用 Ka…

I.MX6ULL内核开发11:使用设备树实现RGB灯驱动

目录 一、实验说明 二、硬件原理图分析 2.1 打开原理图&#xff0c;找到rgb部分 2.2 对RGB的R灯进行寄存器设置 2.2.1 时钟配置 2.2.2 引脚复用GPIO 2.2.3 引脚属性设置 2.2.4 输出电平设置 三、实验代码 3.1 编程思路 3.2 代码分析 3.2.1 添加RGB设备节点 3.2.2 编…

openlayers加载离线地图并实现深色地图

问题背景 我们自己一直使用的openlayergeoserver自己发布的地图&#xff0c;使用的是矢量地图。但是由于政府地图大都使用为天地图&#xff0c;所以需要将geoserver的矢量地图更改为天地图&#xff0c;并且依旧是搭配openlayers来使用。 解决步骤 一&#xff1a;加载离线地图&a…

ubuntu20.04安装nginx一系列问题

当初做一个项目的时候给linux装nginx遇到了很多问题&#xff0c;当初边搞边记录&#xff0c;这两天翻看项目笔记的时候找出来了&#xff0c;就把这一部分分享出来给大家看看 ubuntu20.04 LTS 安装yum无法定位软件包 备份原来的软件源 sudo cp /etc/apt/sources.list /etc/ap…

目前软搭建测试的行业现状和前景

软件测试的发展前景和行业现状 1. 软件测试的工资情况 软件测试的方向&#xff1a;功能>>>接口>>>性能>>>自动化>>>测开>>>人生巅峰 功能测试:曾经互联网缺口和软件测试缺口非常大&#xff0c;因此功能测试越来越多。可是20…

Linux-编写一个自己的命令

前言&#xff08;1&#xff09;在Linux中&#xff0c;我们对文件路径进行操作都需要输入命令。那么&#xff0c;有人可能就会有疑惑了&#xff0c;命令是什么东西&#xff1f;我们是否也可以创造出自己的命令呢&#xff1f;答案是可以的。命令本身其实就是可执行文件。但是与普…

使用docker pull 跨系统架构拉取镜像

使用docker pull 跨系统架构拉取镜像使用docker pull 跨系统架构拉取镜像docker hub上找到相应的镜像在个人电脑中的执行拉取镜像命令&#xff1a;执行查看镜像命令&#xff1a;执行检查镜像命令&#xff1a;执行保存镜像命令&#xff1a;使用docker pull 跨系统架构拉取镜像 …

【C语言编译器】03 Linux GCC 初探

一、准备工作 简单介绍&#xff0c;马上出 GCC 系列。本文非常浅显。 Linux系统常用来用作服务器&#xff0c;其中最常用的发行版是CentOS、Ubuntu、Debian等。 尽管很多C语言IDE都有Linux版本&#xff0c;比如VS、CLion的Linux版。但作为服务器的Linux通常没有GUI界面&…

腾讯TIM实现即时通信 v3+ts实践

目录 初始化sdk 功能描述 初始化 准备 SDKAppID 调用初始化接口 监听事件 发送消息 创建消息 创建文本消息 登录登出 功能描述 登录 登出 销毁 登录设置 获取会话列表 功能描述 获取会话列表 获取全量的会话列表 历史消息 功能描述 拉取消息列表 分页拉取…

自动驾驶自主避障概况

文章目录前言1. 自主避障在自动驾驶系统架构中的位置2. 自主避障算法分类2.1 人工势场法&#xff08;APF&#xff09;2.1.1引力势场的构建2.1.2斥力势场的构建2.1.3人工势场法的改进2.2 TEB&#xff08;Timed-Eastic-Band, 定时弹性带&#xff09;2.3 栅格法2.4 向量场直方图(V…

Linux 之 大数据定制篇-shell 编程

文章目录1 为什么要学习 Shell 编程2 Shell 是什么&#xff1f;3 Shell 脚本的执行方式3.1 脚本格式要求3.2 编写第一个 Shell 脚本3.3 脚本的常用执行方式4 Shell 的变量4.1 Shell 变量介绍4.2 shell 变量的定义4.3 shell 变量的定义5 设置环境变量5.1 基本语法5.2 快速入门6 …

【AI绘画】秒级出图 快速生成大师级画作

最近闲来无事&#xff0c;在网上体验了一下各种AI绘画工具。 根据输入的描述语快速生成自己想要的图片&#xff0c;听着还是很不错的&#xff01;想要啥图片就可以生成啥图片&#xff1f;于是&#xff0c;期待满满的搞起来了~ 可是真当体验了一下之后… 这生成的啥呢&#xf…

广泛运用在工业、轨道交通、监狱的ip对讲终端

ip网络对讲系统是不同于传统广播、调频寻址广播和数控广播的产品&#xff0c;它是基于IP数据网络&#xff0c;将音频信号经过数字编码以数据包形式按TCP\IP协议在局域网或广域网上传送&#xff0c;再由终端解码的纯数字化单向&#xff0c;双向及多向音频扩声系统。 本产品是新一…

多表left join 慢sql问题

作为个人记录&#xff0c;后续再填坑a对p是1对多 ,p对llup 1对多SELECTa.id,p.id,t1.id FROMliv_series_product aINNER JOIN liv_product p ON p.id a.product_idLEFT JOIN ( SELECT llup.id, llup.product_id, llup.room_id FROM liv_live_user_product llup WHERE llup.ro…

超声功率放大器原理(超声功率放大器的作用是什么)

超声功率放大器是电子实验室中比较常见的测量仪器&#xff0c;虽然很多工程师频繁使用&#xff0c;但是对于超声功率放大器的了解却不够。下面就让安泰电子来为大家科普超声功率放大器原理和作用的内容。超声功率放大器是什么&#xff1a;超声功率放大器是一种用于提高超声波能…

requests---(2)session简介与自动写博客

目录&#xff1a;导读 session简介 session登录 自动写博客 获取登录cookies 抓取写博客接口 requests自动写博客 写在最后 http协议是无状态的&#xff0c;也就是每个请求都是独立的。那么登录后的一系列动作&#xff0c;都需要用cookie来验证身份是否是登录状态&#…

C++将派生类赋值给基类(向上转型)

1.将派生类对象赋值给基类对象 #include <iostream> using namespace std;//基类 class A{ public:A(int a); public:void display(); public:int m_a; }; A::A(int a): m_a(a){ } void A::display(){cout<<"Class A: m_a"<<m_a<<endl; }//…