文件下载_上传_分片上传 JavaScript Node.js

文件下载

a.download

文件下载一般使用a标签,当给a标签download属性时,在点击时就会下载访问href请求的文件,但是只有在href指向的地址是同源时才会下载,不同源时只会常规跳转。
download的值就是下载的文件名。

URL.createObjectURL

会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。

以下代码会下载一个html文件。

let str = `
	<div>
		<span>hellowrold</span>
	</div>
`
let blob = new Blob([str], {
  type: 'text/html'
})

const a = document.createElement('a');
a.download = '我的文件';
a.href = URL.createObjectURL(blob);
a.click()

图片预览

利用上面提到的特性可以实现图片预览

<body>
  <input type='file' id='input' />

  <script>
    input.oninput = function (e) {
      const img = new Image()
      img.src = URL.createObjectURL(e.target.files[0]);
      document.body.appendChild(img)
    }
  </script>
</body>

以上创建url的方式时同步的,还可以用FileReader异步读取

<body>
  <input type='file' id='input' />

  <script>
    input.oninput = function (e) {
      const img = new Image()
      const fileReader = new FileReader();
      fileReader.onload = function () {
        img.src = fileReader.result;
      }
      fileReader.readAsDataURL(e.target.files[0])
      document.body.appendChild(img)
    }
  </script>
</body>

文件上传

服务端
const express = require('express');
const path = require('path');
const multiparty = require('multiparty');
const bodyParser = require('body-parser');

const app = express();

app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

router.post('/upload', async (req, res) => {
  const form = new multiparty.Form({
    uploadDir: 'temp',
  });
  form.parse(req);
  form.on('file', () => {
    console.log('上传成功');
  });
  res.send('上传成功');
});

app.listen(3000, () => {
  console.log('connect port 3000');
});
前端
<body>
  <input type='file' id='input' />

  <script>
    input.oninput = function (e) {
      const file = e.target.files[0];
      const formData = new FormData()
      formData.append('file', file)

      axios({
        url: '/upload',
        data: formData,
        method: 'POST',
        headers: {
          "Content-Type": "multipart/form-data"
        }
      }).then(res => {
        index++;
        upload()
      })
    }
  </script>
</body>

分片上传

File对象继承自Blob对象,Blob对象有个方法slice

**Blob.slice() **方法用于创建一个包含源 Blob的指定字节范围内的数据的新 Blob 对象。

所以我们可以使用该方法将文件分割成小块的blob,然后再生成小块的file。
前端代码根据上面的做改动

<body>
  <input type='file' id='input' />

  <script>
    let chunkSize = 1024 * 10, index = 0;
    input.oninput = upload;

    function upload(e) {
      let start = index * chunkSize
      const file = input.files[0];
      if (start > file.size) {
        // 分片上传完成,发送合并分片的请求
        axios.post('/merge', { name: file.name })
        return;
      };

      const [filename, ext] = file.name.split('.')

      const blob = file.slice(start, start + chunkSize)
      const blobName = `${filename}.${index}.${ext}`
      const blobFile = new File([blob], blobName)

      const formData = new FormData()
      formData.append('file', blobFile)

      axios({
        url: '/upload',
        data: formData,
        method: 'POST',
        headers: {
          "Content-Type": "multipart/form-data"
        }
      }).then(res => {
        index++;
        upload()
      })
    }
  </script>
</body>

文件分片上传前端部分就实现了,后端接收到分片文件之后需要拼一下。

const express = require('express');
const path = require('path');
const multiparty = require('multiparty');
const fse = require('fs-extra');
const fs = require('fs');
const bodyParser = require('body-parser');

const app = express();

app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

const UPLOAD_DIR = path.join(__dirname, '/public/upload');

app.post('/upload', async (req, res) => {
  const form = new multiparty.Form({
    uploadDir: 'temp',
  });
  form.parse(req);
  form.on('file', async (name, chunk) => {
    // 存放切片的目录
    let chunkDir = `${UPLOAD_DIR}/${chunk.originalFilename.split('.')[0]}`;
    if (!fse.existsSync(chunkDir)) {
      await fse.mkdirs(chunkDir);
    }

    // 将分片按索引编号再次命名
    let dPath = path.join(chunkDir, chunk.originalFilename.split('.')[1]);
    // 将分片从临时目录移动到分片的存储目录
    await fse.move(chunk.path, dPath, { overwrite: true });
  });
  res.send('上传成功');
});

app.post('/merge', async (req, res) => {
  // 根据传来的文件名进行合并
  let name = req.body.name;
  let fname = name.split('.')[0];

  // 获取文件名的所有分片
  let chunkDir = path.join(UPLOAD_DIR, fname);
  let chunks = await fse.readdir(chunkDir);

  chunks
    .sort((a, b) => a - b)
    .forEach(chunkPath => {
      fs.appendFileSync(
        // 合并文件
        path.join(UPLOAD_DIR, name),
        fs.readFileSync(`${chunkDir}/${chunkPath}`)
      );
    });

  // 合并完成后删除分片目录
  fse.removeSync(chunkDir);
  res.send({
    message: '合并成功',
    url: `http://localhost:3000/upload/${name}`,
  });
});

app.listen(3000, () => {
  console.log('connect port 3000');
});

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值