feat(download): 后端支持 Range 分块; 前端引入多线程分块下载加速音频加载

This commit is contained in:
2025-11-22 23:11:08 +08:00
parent 1ca7a3f610
commit d9dad9d6fd
3 changed files with 80 additions and 3 deletions

View File

@@ -51,6 +51,55 @@ class RemoteFile{
request.responseType = "arraybuffer"
})
}
arrayBufferFast(threads){
var t = threads || 4
var head = new XMLHttpRequest()
head.open("HEAD", this.url)
var headPromise = pageEvents.load(head).then(() => {
if(head.status !== 200){
return Promise.reject()
}
var len = parseInt(head.getResponseHeader("Content-Length"))
if(!len || len < 262144){
return this.arrayBuffer()
}
var chunk = Math.ceil(len / t)
var ranges = []
for(var i = 0; i < t; i++){
var start = i * chunk
var end = Math.min(len - 1, (i + 1) * chunk - 1)
if(start > end){ break }
ranges.push([start, end])
}
var promises = ranges.map(r => {
var req = new XMLHttpRequest()
req.open("GET", this.url)
req.responseType = "arraybuffer"
req.setRequestHeader("Range", "bytes=" + r[0] + "-" + r[1])
return pageEvents.load(req).then(() => {
if(req.status !== 206 && req.status !== 200){
return Promise.reject()
}
return req.response
})
})
return Promise.all(promises).then(parts => {
var total = 0
for(var i = 0; i < parts.length; i++){
total += parts[i].byteLength
}
var out = new Uint8Array(total)
var offset = 0
for(var i = 0; i < parts.length; i++){
out.set(new Uint8Array(parts[i]), offset)
offset += parts[i].byteLength
}
return out.buffer
})
})
head.send()
return headPromise.catch(() => this.arrayBuffer())
}
read(encoding){
if(encoding){
return this.blob().then(blob => readFile(blob, false, encoding))

View File

@@ -1,4 +1,4 @@
class SoundBuffer{
class SoundBuffer{
constructor(...args){
this.init(...args)
}
@@ -12,7 +12,8 @@
}
load(file, gain){
var decoder = file.name.endsWith(".ogg") ? this.oggDecoder : this.audioDecoder
return file.arrayBuffer().then(response => {
var promise = typeof file.arrayBufferFast === "function" ? file.arrayBufferFast(4) : file.arrayBuffer()
return promise.then(response => {
return new Promise((resolve, reject) => {
return decoder(response, resolve, reject)
}).catch(error => Promise.reject([error, file.url]))