微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何避免 Firefox 下载的“另存为”对话框?

如何解决如何避免 Firefox 下载的“另存为”对话框?

我正在为网站创建批量下载功能。它适用于 chrome,但 Firefox 中的另存为对话框给我带来了问题。由于我正在循环下载大量文件,因此它会打开相同数量的对话框(除非他们快速选择保存为第一次下载。)有没有办法解决这个问题?

理想情况下,我希望在选择另存为对话时触发一个事件,或者可能是一种了解它的简单方法我当前文件类型的认行为是“每次询问”或“另存为”并提示用户在下载开始之前更改它。但据我所知,这些功能并不存在(如果我错了,请纠正我)。

请注意,我无权访问服务器,因此任何类型的服务器端解决方案都不适合我。我也无法在客户端压缩文件,因为这些文件可能很大。

仅供参考,虽然我认为这在这里不是很有帮助,但以下是我的 Downloader 类,这里感兴趣的方法_savetodisk()

class Downloader{

  /**
   * 
   * @param {Array<string>} downloadLinks 
   */
  constructor(downloadLinks){
    //downloadConstants
    this.bufferSize =
      downloadConstants.bufferSize
    this.noOfActiveFetchLimit =
      downloadConstants.noOfActiveFetchLimit
    this.fallbackNoOfActiveFetchLimit =
      downloadConstants.fallbackNoOfActiveFetchLimit
    this.downloadInterval =
      downloadConstants.downloadInterval
    this.fallbackDownloadInterval =
      downloadConstants.fallbackDownloadInterval

    this.expectedBufferUsed = 0
    this.noOfActiveFetch = 0
    this.downloadedCount = 0
    this.abortedCount = 0
    this.downloadJobCount =
      downloadLinks?.length ? downloadLinks.length : 0

    this.isDownloadOngoing = false
    this.iteratedOnce = false
    this.lastFiveIsEqual = false
    this.hasSizeInfo = true
    
    this.downloadLinks = downloadLinks ?? []
    this.FailedDownloads = {
      retry : [],//with status 0
      errorlog: []  //status >= 400
    }
    this.queue = new DownloadQueue()
    this.lastFive = new LastFive()
    this.lastFiveOnloadedSize = new LastFive()
  }

  _isFallBack(){
    return (
    this.expectedBufferUsed > this.bufferSize ||
    !this.hasSizeInfo &&
    (
      !this.lastFiveOnloadedSize.getAvgSize() ?
      true :
      this.lastFiveOnloadedSize.getAvgSize() > this.bufferSize
    ))
  }

  _isDownloadComplete(){
    return (this.downloadedCount + this.abortedCount
      === this.downloadJobCount)
  }

  _shouldAbort(size){
    return (this.bufferSize - this.expectedBufferUsed <= size &&
    !this.iteratedOnce &&
    !this.lastFiveIsEqual &&
    !this.lastFiveOnloadedSize.allEqual())
  }

  _getNoOfActiveFetchLimit(){
    return !this._isFallBack() ?
      this.noOfActiveFetchLimit : this.fallbackNoOfActiveFetchLimit
  }

  _getDownloadInterval(){
    return !this._isFallBack() ?
      this.downloadInterval : this.fallbackDownloadInterval
  }

  _addExpectedBufferUsed(size){

    if (!size){
      if(this.lastFiveOnloadedSize.allEqual()){
        size = this.lastFiveOnloadedSize.array[4]
      }else if(this.lastFiveOnloadedSize.getAvgSize()){
       size = this.lastFiveOnloadedSize.getAvgSize()
      }else{
        size = 0
      }
    }

    this.expectedBufferUsed += size

    //for cases when size info isn't available
    if(this.expectedBufferUsed < 0){
      this.expectedBufferUsed = 0
    }
  }

  _abortHandler(xhr,downloadLink,size){
     //if lastFive elements have same size
      //we assume that all the requests will be the same
      //until proven differently

      xhr.abort()
      this.queue.push({
        url: downloadLink,size
      })
      this.lastFive.push(size)      
      this.abortedCount++
      this.noOfActiveFetch--
      return
  }

  _requestSmallerFile(){
    if(this.noOfActiveFetch >= this._getNoOfActiveFetchLimit()){
      return false
    }

    const fetchSize = this.bufferSize - this.expectedBufferUsed
    const newRequestItem = this.queue.pop(fetchSize)
    
    if(newRequestItem != undefined){
      this._fetch(newRequestItem.url)
      return true
    }

    return false
  }

  _filterRequests(xhr,downloadLink){
    const size = xhr.getResponseHeader("Content-length") ?
      parseInt(xhr.getResponseHeader("Content-length")) :
      null

    this.hasSizeInfo = size ? true : false
    
    this.lastFive.push(size)
    this.lastFiveIsEqual = this.lastFive.allEqual()

    if(this._shouldAbort(size)){
      this._abortHandler(xhr,size,downloadLink)
      this._requestSmallerFile()
      return
    }

    this._addExpectedBufferUsed(size)
  }

  _savetodisk(res,downloadLink){
    this.lastFiveOnloadedSize.push(res.loaded)
  
    const blobURL = window.URL.createObjectURL(res)
    const a = document.createElement('a')  
    const fileName = downloadLink.substr(downloadLink.lastIndexOf('/') + 1)
    
    a.href = blobURL
    a.setAttribute('download',fileName)  
    
    document.body.appendChild(a)  
    a.click()
    
    a.remove()  
    window.URL.revokeObjectURL(blobURL)
  }

  _updateDownloadParams(loaded){
     //deduct the memory freed here
    this._addExpectedBufferUsed((-1)*loaded)

    this.noOfActiveFetch--
    this.downloadJobCount++
  }

  _handleErrors(xhr,downloadLink){
    const status = xhr.status
    if(status === 0){
      this.FailedDownloads.retry.push(downloadLink)
    } else{
      this.FailedDownloads.errorlog.push({
        status,url: downloadLink
      })
    }
  }

  _retryFailedDownloads(){
    //under construction
  }

  _onload(xhr,res,downloadLink){
    const status = xhr.status
    
    if(status >= 200 && status < 400){
      this._savetodisk(xhr.response,downloadLink)
      this._updateDownloadParams(res.loaded)
    } else {
      this._handleErrors(xhr,downloadLink)
    }

    if(this._isDownloadComplete()){
      this._retryFailedDownloads()
    }
  }

  _fetch(downloadLink){
    const xhr = new XMLHttpRequest()
    xhr.overrideMimeType('application/octet-stream')

    xhr.onreadystatechange = res => {

      const readyState = xhr.readyState
      
      if (readyState === 1){
        this.fetchCount += 1
        this.noOfActiveFetch += 1
        return
      } else if (readyState === 2){
        this._filterRequests(xhr,downloadLink)
      } else if (readyState === 3){
        return
      } else if (readyState === 4){
        this._onload(xhr,downloadLink)
      }
  
    }
    
    xhr.open('GET',downloadLink)
    xhr.responseType = 'blob'
    xhr.send(null)
  }

  _initiateSecondDownloadIteration(){
    this.iteratedOnce = true  
    if (this.queue.length == 0){
      return
    }
    this.downloadLinks = this.downloadLinks.concat(this.queue.getURLs())
    this.queue.reset()
    this._initiateDownload()
    return
  }

  _initiateDownload(){
    
    const setDownloadInterval = () => {
      if (this.downloadLinks.length === 0){
        this._initiateSecondDownloadIteration()
        return
      }

      if(this.noOfActiveFetch >= this._getNoOfActiveFetchLimit()){
        setTimeout(setDownloadInterval,this._getDownloadInterval() + this.fallbackDownloadInterval)
        return
      }
  
      this._fetch(`${fileServerBaseUrl}/${directoryEndpoint}/${this.downloadLinks.pop()}`)
      setTimeout(setDownloadInterval,this._getDownloadInterval())
    }
    setTimeout(setDownloadInterval,0)
  }

  /**
   * 
   * @param {Array.<string>} downloadLinks 
   */
  download(downloadLinks){
    if(downloadLinks != undefined){
      this.downloadLinks = this.downloadLinks.concat(downloadLinks)
      this.downloadJobCount +=
        this.downloadLinks?.length ? downloadLinks.length : 0
    }
    
    if(!this.isDownloadOngoing){
      this.isDownloadOngoing = true
      this._initiateDownload()
      return
    }

    return
  }

   _reset(){
    this.expectedBufferUsed = 0
    this.noOfActiveFetch = 0

    this.downloadedCount = 0
    this.abortedCount = 0
    this.downloadJobCount = 0
    
    this.isDownloadOngoing = false
    this.iteratedOnce = false
    this.lastFiveIsEqual = false
    this.hasSizeInfo = true
    
    this.downloadLinks = []
    this.FailedDownloads = {
      retry : [],errorlog: []
    }
    this.queue.reset()
    this.lastFive.reset()
    this.lastFiveOnloadedSize.reset()
  }

}

解决方法

“选项”页面上有一个“每次都问我”保存下载的位置的设置,只需将其设置为“从不”

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。