当前位置: 首页 > article >正文

学习node.js十三,文件的上传于下载

文件上传

文件上传的方案:

  1. 大文件上传:将大文件切分成较小的片段(通常称为分片或块),然后逐个上传这些分片。这种方法可以提高上传的稳定性,因为如果某个分片上传失败,只需要重新上传该分片而不需要重新上传整个文件。同时,分片上传还可以利用多个网络连接并行上传多个分片,提高上传速度
  2. 断点续传:在上传过程中,如果网络中断或上传被中止,断点续传技术可以记录已成功上传的分片信息,以便在恢复上传时继续上传未完成的部分,而不需要重新上传整个文件。这种技术可以大大减少上传失败的影响,并节省时间和带宽。
前端实现
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        input {
            background-color: #f5f5f5;
            border: 1px solid #ccc;
            border-radius: 5px;
            cursor: pointer;
            outline: none;
            font-size: 14px;
            color: #333;
            text-align: center;
            line-height: 30px;
            font-size: 40px;
        }
    </style>
</head>
<body>
    <!--  上传文件  -->
    <input type="file" id="file" name="file" value="上传文件" />
</body>

第一步:获取元素,监听change事件。获取到文件的信息之后,利用file原型上面的 blob对象的slice方法来进行分割

// 获取文件,监听有无上传
    const file = document.getElementById('file');
    file.addEventListener('change', function (e) {
        // 获取文件信息
        const file = e.target.files[0];
        const chunks = sliceFile(file);
        uploadFile(chunks)
    })
    // 分片
    function sliceFile(file, chunkSize = 1024 * 1024 * 3) {
        let chunks = []
        for (let i = 0; i < file.size; i+= chunkSize) {
            chunks.push(file.slice(i, i + chunkSize))
        }
        return chunks
    }

第二步:将这些分片的文件片,编入编号和文件名后以formData的格式上传,并且将结果放入promise.all这个方法中,如果全部成功的化,那么就调用合并函数,将这个视频进行合并

// 上传
    function uploadFile(chunks) {
        let list = []
        for (let i = 0; i < chunks.length; i++) {
            let formData = new FormData();
            formData.append('index', i)
            formData.append('name', "wenjian")
            formData.append('file', chunks[i])
            list.push(fetch("http://localhost:8080/upload", {
                method: 'POST',
                body: formData
            }))
        }
        // 监听事件是否成功
        Promise.all(list).then(res => {
            // 发送合并请求
            fetch("http://localhost:8080/merge", {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    name: "ceshi.gif",
                })
            }).then(res => {
                console.log(res)
            }).catch(e => {
                console.log(e)
            })
        }).catch(e=> {
            console.log(e)
        })
    }
nodejs端实现

安装依赖

  1. express 帮我们启动服务,并且提供接口
  2. multer 读取文件,存储
  3. cors 解决跨域

初始化 multer.diskStorage

  • destination 存储的目录
  • filename 存储的文件名(我是通过index-文件名存储的你也可以改)
// 1. 初始化multer
const storage = multer.diskStorage({
    destination:function (req,file,cb) {
        cb(null,'./upload')
    },
    filename:function (req,file,cb) {
        cb(null,`${req.body.index}-${req.body.name}`)
    }
})

放到接口上面,就可以将分完片的文件上传

// 2. 配置multer
const upload = multer({storage:storage})
// 3. 创建上传接口
app.post('/upload',upload.single('file'),(req,res) => {
    res.send('上传成功')
})

合并文件:先读取分片文件的文件名,然后把这些文件重新的进行排序,合成一个新的文件

完整代码:

import express from 'express'
import multer from 'multer'
import cors from 'cors'
import fs from 'node:fs'
import path from 'node:path'

// 1. 初始化multer
const storage = multer.diskStorage({
    destination:function (req,file,cb) {
        cb(null,'./upload')
    },
    filename:function (req,file,cb) {
        cb(null,`${req.body.index}-${req.body.name}`)
    }
})
const app = express()
app.use(cors())
app.use(express.json())
// 2. 配置multer
const upload = multer({storage:storage})
// 3. 创建上传接口
app.post('/upload',upload.single('file'),(req,res) => {
    res.send('上传成功')
})
// 4. 合并文件
app.post("/merge",(req,res) => {
    if(!req.body.name) return res.send('文件名不能为空')
    let uploadDir = "./upload"
    // 读取分片文件
    let files = fs.readdirSync(path.join(process.cwd(), uploadDir))
    // 重新排序
    files = files.sort((a,b) => a.split('-')[0] - b.split('-')[0])
    // 合并文件
    let writeDir = path.join(process.cwd(),"./video",`${req.body.name}`)
    files.forEach(item => {
        fs.appendFileSync(writeDir,fs.readFileSync(path.join(process.cwd(),uploadDir,item)))
        fs.unlinkSync(path.join(process.cwd(),uploadDir,item))
    })
    res.send('合并成功')
})
app.listen(8080,() => console.log('Server is running on port 8080'))

文件流下载

文件流下载是一种通过将文件内容以流的形式发送给客户端,实现文件下载的方法。它适用于处理大型文件或需要实时生成文件内容的情况。

nodejs端实现

响应头

  1. Content-Type 指定下载文件的 MIME 类型
    • application/octet-stream(二进制流数据)
    • application/pdf:Adobe PDF 文件。
    • application/json:JSON 数据文件
    • image/jpeg:JPEG 图像文件
  2. Content-Disposition 指定服务器返回的内容在浏览器中的处理方式。它可以用于控制文件下载、内联显示或其他处理方式
    • attachment:指示浏览器将响应内容作为附件下载。通常与 filename 参数一起使用,用于指定下载文件的名称
    • inline:指示浏览器直接在浏览器窗口中打开响应内容,如果内容是可识别的文件类型(例如图片或 PDF),则在浏览器中内联显

代码实现:

import express from 'express'
import fs from 'fs'
import path from 'path'
import cors from 'cors'

const app = express()
app.use(cors())
app.use(express.json())
app.use(express.static(path.join(process.cwd(),"static")))

app.post("/upload", (req, res) => {
  let fileName = req.body.fileName
  if(!fileName) return res.send({message: "File name is required"})
  let filePath = path.join(process.cwd(),"static", fileName)
  let readStream = fs.readFileSync(filePath)
  // 设置响应头
  res.setHeader('Content-Type', 'application/octet-stream')
  res.setHeader('Content-Disposition', 'attachment;filename=' + fileName)
  res.send(readStream)
})

app.listen(3000,() => console.log('Server is running on port 3000'))

前端逻辑

前端核心逻辑就是接受的返回值是流的方式arrayBuffer,转成blob,生成下载链接,模拟a标签点击下载

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        input {
            background-color: #f5f5f5;
            border: 1px solid #ccc;
            border-radius: 5px;
            cursor: pointer;
            outline: none;
            font-size: 14px;
            color: #333;
            text-align: center;
            line-height: 30px;
            font-size: 40px;
        }
    </style>
</head>
<body>
    <!--  上传文件  -->
    <input type="button"  value="下载文件" />
</body>
<script>
   let btn = document.querySelector('input');
   btn.onclick = function () {
       fetch("http://localhost:3000/upload",{
           method:'POST',
           headers:{
               'Content-Type':'application/json'
           },
           body:JSON.stringify({
               fileName:'1.png',
           })
       }).then(res => res.arrayBuffer()).then(res => {
           // 1. 转为blob
           let blob = new Blob([res],{type:'image/png'})
           // 2. 创建a标签
           let a = document.createElement('a')
           // 4. 设置a标签的href属性为blob地址
           a.href = URL.createObjectURL(blob)
           // 3. 设置下载文件名
           a.download = '1.png'
           // 5. 模拟点击a标签
           a.click()
           // 6. 移除a标签
           a.remove()
       })
   }
</script>
</html>

http://www.kler.cn/a/292419.html

相关文章:

  • 学习threejs,使用TWEEN插件实现动画
  • HarmonyOS Next星河版笔记--界面开发(5)
  • 使用 Prompt API 与您的对象聊天
  • Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】
  • linux常见资源查询命令(持续更新)
  • IOT物联网低代码可视化大屏解决方案汇总
  • C++:STL简介
  • GFP-GAN容器构建说明
  • 数据分析面试常见50个问题及解答要点(五)
  • Curl命令详解
  • 【全志H616】【开源】 ARM-Linux 智能分拣项目:阿里云、网络编程、图像识别
  • SQL server数据库实现远程跨服务器定时同步传输数据
  • 举例说明,在python中怎样使用哈希算法?
  • vue3+ts封装类似于微信消息的组件
  • 车载测试协议:ISO-14229、ISO-15765、ISO-11898、ISO-26262【车企项目实操学习】②
  • 贪吃蛇的小游戏--用C语言实现
  • 每天1亿Amazon EC2实例稳定启动背后:解密亚马逊云科技如何构建可靠的云服务
  • B树和B+树
  • J.U.C Review - 常见的通信工具类解析
  • 【在GEE中计算NDVI*1】
  • 微信小程序-文件下载
  • JavaEE:多线程进阶(CAS)
  • 护眼台灯防蓝光很重要吗?推荐五款防蓝光效果好的护眼台灯
  • UAEXpert连接kepserver的OPC服务时,出现BadCertificateHostNamelnvalid报错--解决办法
  • C++ 模板进阶知识——万能引用
  • 汽车免拆诊断案例 | 捷豹 E-type怠速不稳定