效果(比较下载速度)
服务端
请求响应头
源码
const express = require ( 'express' ) ;
const path = require ( "path" ) ;
const fs = require ( "fs" ) ;
const router = express. Router ( ) ;
const fileName = '下载文件.zip' ;
const filePath = path. join ( __dirname, ` ../public/ ${ fileName} ` ) ;
router. get ( '/size' , ( req, res, next ) => {
res. setHeader ( 'Access-Control-Allow-Origin' , '*' ) ;
res. json ( { code : 200 , data : { fileName, fileSize : fs. statSync ( filePath) . size} , message : '请求成功!' } ) ;
} ) ;
router. get ( '/download' , ( req, res, next ) => {
res. setHeader ( 'Access-Control-Allow-Origin' , '*' ) ;
res. download ( filePath, fileName, { acceptRanges : true } ) ;
} ) ;
module. exports = router;
客户端
<! doctype html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< meta name = " viewport" content = " width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" >
< meta http-equiv = " X-UA-Compatible" content = " ie=edge" >
< title> <%= title %></ title>
</ head>
< style>
div.progress {
width : 0;
margin : 10px;
height : 20px;
padding : 0 10px;
font-size : 12px;
line-height : 20px;
white-space : nowrap;
border : 1px solid #cccccc;
background-color : greenyellow;
}
</ style>
< body>
< button onclick = " basicDownloadFile ( ) ; fragmentDownloadFile ( ) ; " > 下载文件(普通、分片)</ button>
< script>
function request ( { method, url, headers = { } , responseType = undefined } ) {
let div = null ;
let width = 0 ;
return new Promise ( ( resolve, reject ) => {
const xhr = new XMLHttpRequest ( ) ;
xhr. open ( method, url) ;
for ( const key in headers) {
xhr. setRequestHeader ( key, headers[ key] ) ;
}
if ( responseType) {
xhr. responseType = responseType;
}
xhr. send ( ) ;
xhr. onreadystatechange = ( e ) => {
if ( e. target. readyState === 2 ) {
div = document. createElement ( 'div' ) ;
div. setAttribute ( 'class' , 'progress' ) ;
div. innerText = ` 用时 -> ${ method} ${ url} ${ headers. Range || '' } ` ;
document. body. appendChild ( div) ;
}
if ( e. target. readyState === 3 ) {
width++ ;
div. style. width = ` ${ width} px ` ;
}
if ( e. target. readyState === 4 ) {
if ( e. target. status >= 200 && e. target. status < 300 ) {
console. log ( width) ;
resolve ( e. target. response) ;
}
}
}
} )
}
function basicDownloadFile ( ) {
request ( {
method : 'GET' ,
url : ` http://127.0.0.1:3000/file/download?_t= ${ Date. now ( ) } ` ,
responseType : 'arraybuffer' ,
} )
. then ( res => {
const blob = new Blob ( [ res] ) ;
const a = document. createElement ( 'a' ) ;
const href = URL . createObjectURL ( blob) ;
a. href = href;
a. download = '下载文件.zip' ;
document. body. appendChild ( a) ;
a. click ( ) ;
document. body. removeChild ( a) ;
window. URL . revokeObjectURL ( href) ;
console. log ( '下载完成01' )
} )
}
async function fragmentDownloadFile ( ) {
let res = await request ( { method : 'GET' , url : ` http://127.0.0.1:3000/file/size?_t= ${ Date. now ( ) } ` } ) ;
const { fileSize, fileName} = JSON . parse ( res) . data;
console. log ( '文件名称' , fileName) ;
console. log ( '文件大小' , fileSize) ;
const step = Math. pow ( 8 , 6 ) ;
console. log ( '步长' , step) ;
let tasks = [ ] ;
for ( let i = 0 ; i < Math. ceil ( fileSize / step) ; ) {
let [ start, end] = [ i * step, Math. min ( ++ i * step - 1 , fileSize) ] ;
console. log ( [ start, end] ) ;
tasks. push ( request ( {
method : 'GET' ,
url : ` http://127.0.0.1:3000/file/download?_t= ${ Date. now ( ) } ` ,
headers : {
Range : ` bytes= ${ start} - ${ end} `
} ,
responseType : 'arraybuffer'
} ) ) ;
}
console. log ( '步长切割的请求集合' , tasks) ;
const arrayBuffers = await Promise. all ( tasks) ;
console. log ( '原始二进制数据缓冲区集合' , arrayBuffers) ;
const uint8Array = new Uint8Array ( fileSize) ;
let offset = 0 ;
arrayBuffers. forEach ( arrayBuffer => {
uint8Array. set ( new Uint8Array ( arrayBuffer) , offset) ;
offset += arrayBuffer. byteLength;
} ) ;
console. log ( 'ArrayBuffer' , uint8Array. buffer) ;
const blob = new Blob ( [ uint8Array. buffer] ) ;
const a = document. createElement ( 'a' ) ;
const href = URL . createObjectURL ( blob) ;
a. href = href;
a. download = fileName;
document. body. appendChild ( a) ;
a. click ( ) ;
document. body. removeChild ( a) ;
window. URL . revokeObjectURL ( href) ;
console. log ( '下载完成02' )
}
</ script>
</ body>
</ html>