【前端】智能库房综合管理系统前端项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
3.2 KiB

  1. import { Now } from '../../utils'
  2. export default class AjaxProgressiveSource {
  3. constructor(url, options) {
  4. this.url = url
  5. this.destination = null
  6. this.request = null
  7. this.streaming = false
  8. this.completed = false
  9. this.established = false
  10. this.progress = 0
  11. this.fileSize = 0
  12. this.loadedSize = 0
  13. this.chunkSize = options.chunkSize || 1024 * 1024
  14. this.isLoading = false
  15. this.loadStartTime = 0
  16. this.throttled = options.throttled !== false
  17. this.aborted = false
  18. this.onEstablishedCallback = options.onSourceEstablished
  19. this.onCompletedCallback = options.onSourceCompleted
  20. }
  21. connect(destination) {
  22. this.destination = destination
  23. }
  24. start() {
  25. this.request = new XMLHttpRequest()
  26. this.request.onreadystatechange = function() {
  27. if (this.request.readyState === this.request.DONE) {
  28. this.fileSize = parseInt(
  29. this.request.getResponseHeader('Content-Length')
  30. )
  31. this.loadNextChunk()
  32. }
  33. }.bind(this)
  34. this.request.onprogress = this.onProgress.bind(this)
  35. this.request.open('HEAD', this.url)
  36. this.request.send()
  37. }
  38. resume(secondsHeadroom) {
  39. if (this.isLoading || !this.throttled) {
  40. return
  41. }
  42. // Guess the worst case loading time with lots of safety margin. This is
  43. // somewhat arbitrary...
  44. const worstCaseLoadingTime = this.loadTime * 8 + 2
  45. if (worstCaseLoadingTime > secondsHeadroom) {
  46. this.loadNextChunk()
  47. }
  48. }
  49. destroy() {
  50. this.request.abort()
  51. this.aborted = true
  52. }
  53. loadNextChunk() {
  54. const start = this.loadedSize
  55. const end = Math.min(this.loadedSize + this.chunkSize - 1, this.fileSize - 1)
  56. if (start >= this.fileSize || this.aborted) {
  57. this.completed = true
  58. if (this.onCompletedCallback) {
  59. this.onCompletedCallback(this)
  60. }
  61. return
  62. }
  63. this.isLoading = true
  64. this.loadStartTime = Now()
  65. this.request = new XMLHttpRequest()
  66. this.request.onreadystatechange = function() {
  67. if (
  68. this.request.readyState === this.request.DONE &&
  69. this.request.status >= 200 &&
  70. this.request.status < 300
  71. ) {
  72. this.onChunkLoad(this.request.response)
  73. } else if (this.request.readyState === this.request.DONE) {
  74. // Retry?
  75. if (this.loadFails++ < 3) {
  76. this.loadNextChunk()
  77. }
  78. }
  79. }.bind(this)
  80. if (start === 0) {
  81. this.request.onprogress = this.onProgress.bind(this)
  82. }
  83. this.request.open('GET', this.url + '?' + start + '-' + end)
  84. this.request.setRequestHeader('Range', 'bytes=' + start + '-' + end)
  85. this.request.responseType = 'arraybuffer'
  86. this.request.send()
  87. }
  88. onProgress(ev) {
  89. this.progress = ev.loaded / ev.total
  90. }
  91. onChunkLoad(data) {
  92. const isFirstChunk = !this.established
  93. this.established = true
  94. this.progress = 1
  95. this.loadedSize += data.byteLength
  96. this.loadFails = 0
  97. this.isLoading = false
  98. if (isFirstChunk && this.onEstablishedCallback) {
  99. this.onEstablishedCallback(this)
  100. }
  101. if (this.destination) {
  102. this.destination.write(data)
  103. }
  104. this.loadTime = Now() - this.loadStartTime
  105. if (!this.throttled) {
  106. this.loadNextChunk()
  107. }
  108. }
  109. }