【问题标题】:How to avoid "save as" dialogue box for firefox downloads?如何避免Firefox下载的“另存为”对话框?
【发布时间】:2021-06-16 15:44:09
【问题描述】:

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

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

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

仅供参考,虽然我认为这里不是很有帮助,但以下是我的下载器类,这里感兴趣的方法是_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, res, 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()
  }

}

【问题讨论】:

  • 转到Firefox设置并搜索下载。应该有“总是问你在哪里保存文件”的复选框,取消选择它并选择一个固定的位置。
  • @Simon 我不是在谈论询问下载位置的对话框。我说的是询问是否打开或保存下载文件的对话框。

标签: javascript firefox download save-as


【解决方案1】:

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

【讨论】:

  • 我检查过了。选项页面只会显示已经下载过一次的文件格式。并且用户不能在列表中添加任何新的文件格式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 2018-01-30
  • 2012-08-22
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
相关资源
最近更新 更多