UNPKG

ledown

Version:

A library for nodejs to download http large file in pauseable and resumable way

150 lines (130 loc) 3.71 kB
export type ByteRange = [number, number] const cloneByteRange = data => { if (Array.isArray(data)) { return data.map(cloneByteRange) } return data } export default class ByteSheet { /** * @property indicates the range of bytes which remain to be download */ remaining: ByteRange[] = [] /** z * @property indicates the range of bytes which are being downloading */ downloading: ByteRange[] = [] /** * @property indicates the range of bytes which are finished downloading */ completed: ByteRange[] = [] constructor (arg: number | Array<ByteRange[]> = 0) { if (typeof arg === 'number') { this.remaining.push([0, arg]) } else { const clone = cloneByteRange(arg) this.remaining = clone[0] this.downloading = [] this.completed = clone[1] } } get completedByteCount (): number { return this.getByteCountOf(this.completed) } get downloadingByteCount (): number { return this.getByteCountOf(this.downloading) } get remainingByteCount (): number { return this.getByteCountOf(this.remaining) } getByteCountOf (section: ByteRange[]): number { return section.reduce((bytes: number, item: ByteRange) => { return bytes + item[1] - item[0] }, 0) } toJSONObject (): Array<ByteRange[]> { const remaining = cloneByteRange(this.remaining) const downloading = cloneByteRange(this.downloading) const completed = cloneByteRange(this.completed) return [ this.resolveRange([ ...remaining, ...downloading ]), completed ] } /** * resolve an array of byte range */ resolveRange (ranges: ByteRange[]): ByteRange[] { return ranges .filter(range => range.length > 0 && range[1] > range[0]) .sort((prev, next) => prev[0] - next[0]) .reduce((sum: ByteRange[], range) => { const last = sum[sum.length - 1] if (last && last[1] >= range[0]) { last[1] = last[1] > range[1] ? last[1] : range[1] } else { sum.push(range) } return sum }, []) } /** * take range of size from remaining byte range * @param {number} size */ allocRangeToDownload (size: number): ByteRange { const firstRange = this.remaining[0] if (!firstRange) return null const start = firstRange[0] const end = Math.min(start + size, firstRange[1]) const range = [start, end] this.remaining = this.cutSection(range, this.remaining) this.downloading = this.resolveRange([ ...this.downloading, range ]) return range } setRangeAsCompleted (range: ByteRange): void { this.downloading = this.cutSection(range, this.downloading) this.completed = this.resolveRange([ ...this.completed, range ]) } cutSection (cut: ByteRange, section: ByteRange[]): ByteRange[] { return this.resolveRange(section.reduce((sec: ByteRange[], item) => { let range = [...item] sec.push(range) // Range: |--------| // Cut: |---| if (cut[0] > range[0] && cut[1] < range[1]) { sec.push([cut[1], range[1]]) range[1] = cut[0] } // Range: |--------| // Cut: |----| // Cut: |------------| if (cut[0] <= range[0] && cut[1] > range[0]) { range[0] = cut[1] } // Range: |--------| // Cut: |----| // Cut: |-------------| if (cut[1] >= range[1] && cut[0] < range[1]) { range[1] = cut[0] } return sec }, [])) } resetDownloading (): void { this.remaining = this.resolveRange([ ...this.remaining, ...this.downloading ]) this.downloading = [] } }