feat: upload queue
							parent
							
								
									c9cc0d3d5d
								
							
						
					
					
						commit
						6ec6a23861
					
				| 
						 | 
				
			
			@ -1,34 +1,95 @@
 | 
			
		|||
import Vue from 'vue'
 | 
			
		||||
import { files as api } from '@/api'
 | 
			
		||||
import throttle from 'lodash.throttle'
 | 
			
		||||
import buttons from '@/utils/buttons'
 | 
			
		||||
 | 
			
		||||
const UPLOADS_LIMIT = 5;
 | 
			
		||||
 | 
			
		||||
const state = {
 | 
			
		||||
  id: 0,
 | 
			
		||||
  count: 0,
 | 
			
		||||
  size: 0,
 | 
			
		||||
  progress: []
 | 
			
		||||
  progress: [],
 | 
			
		||||
  queue: [],
 | 
			
		||||
  uploads: {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const mutations = {
 | 
			
		||||
  incrementId: (state) => {
 | 
			
		||||
    state.id = state.id + 1
 | 
			
		||||
  },
 | 
			
		||||
  incrementSize: (state, value) => {
 | 
			
		||||
    state.size = state.size + value
 | 
			
		||||
  },
 | 
			
		||||
  incrementCount: (state) => {
 | 
			
		||||
    state.count = state.count + 1
 | 
			
		||||
  },
 | 
			
		||||
  decreaseCount: (state) => {
 | 
			
		||||
    state.count = state.count - 1
 | 
			
		||||
  },
 | 
			
		||||
  setProgress(state, { id, loaded }) {
 | 
			
		||||
    Vue.set(state.progress, id, loaded)
 | 
			
		||||
  },
 | 
			
		||||
  reset: (state) => {
 | 
			
		||||
    state.id = 0
 | 
			
		||||
    state.size = 0
 | 
			
		||||
    state.count = 0
 | 
			
		||||
    state.progress = []
 | 
			
		||||
  },
 | 
			
		||||
  addJob: (state, item) => {
 | 
			
		||||
    state.queue.push(item)
 | 
			
		||||
    state.size += item.file.size
 | 
			
		||||
    state.id++
 | 
			
		||||
  },
 | 
			
		||||
  moveJob(state) {
 | 
			
		||||
    const item = state.queue[0]
 | 
			
		||||
    state.queue.shift()
 | 
			
		||||
    Vue.set(state.uploads, item.id, item)
 | 
			
		||||
  },
 | 
			
		||||
  removeJob(state, id) {
 | 
			
		||||
    delete state.uploads[id]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default { state, mutations, namespaced: true }
 | 
			
		||||
const actions = {
 | 
			
		||||
  upload: (context, item) => {
 | 
			
		||||
    let uploadsCount = Object.keys(context.state.uploads).length;
 | 
			
		||||
 | 
			
		||||
    let isQueueEmpty = context.state.queue.length == 0
 | 
			
		||||
    let isUploadsEmpty = uploadsCount == 0
 | 
			
		||||
 | 
			
		||||
    if (isQueueEmpty && isUploadsEmpty) {
 | 
			
		||||
      buttons.loading('upload')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    context.commit('addJob', item)
 | 
			
		||||
    context.dispatch('processUploads')
 | 
			
		||||
  },
 | 
			
		||||
  finishUpload: (context, item) => {
 | 
			
		||||
    context.commit('setProgress', { id: item.id, loaded: item.file.size })
 | 
			
		||||
    context.commit('removeJob', item.id)
 | 
			
		||||
    context.dispatch('processUploads')
 | 
			
		||||
  },
 | 
			
		||||
  processUploads: async (context) => {
 | 
			
		||||
    let uploadsCount = Object.keys(context.state.uploads).length;
 | 
			
		||||
 | 
			
		||||
    let isBellowLimit = uploadsCount < UPLOADS_LIMIT
 | 
			
		||||
    let isQueueEmpty = context.state.queue.length == 0
 | 
			
		||||
    let isUploadsEmpty = uploadsCount == 0
 | 
			
		||||
 | 
			
		||||
    let isFinished = isQueueEmpty && isUploadsEmpty
 | 
			
		||||
    let canProcess = isBellowLimit && !isQueueEmpty
 | 
			
		||||
 | 
			
		||||
    if (isFinished) {
 | 
			
		||||
      buttons.success('upload')
 | 
			
		||||
      context.commit('reset')
 | 
			
		||||
      context.commit('setReload', true, { root: true })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (canProcess) {
 | 
			
		||||
      const item = context.state.queue[0];
 | 
			
		||||
      context.commit('moveJob')
 | 
			
		||||
 | 
			
		||||
      if (item.file.isDir) {
 | 
			
		||||
        await api.post(item.path).catch(Vue.prototype.$showError)
 | 
			
		||||
      } else {
 | 
			
		||||
        let onUpload = throttle(
 | 
			
		||||
          (event) => context.commit('setProgress', { id: item.id, loaded: event.loaded }),
 | 
			
		||||
          100, { leading: true, trailing: false }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        await api.post(item.path, item.file, item.overwrite, onUpload).catch(Vue.prototype.$showError)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      context.dispatch('finishUpload', item)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default { state, mutations, actions, namespaced: true }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,4 @@
 | 
			
		|||
import store from '@/store'
 | 
			
		||||
import { files as api } from '@/api'
 | 
			
		||||
import throttle from 'lodash.throttle'
 | 
			
		||||
import buttons from '@/utils/buttons'
 | 
			
		||||
import url from '@/utils/url'
 | 
			
		||||
 | 
			
		||||
export function checkConflict(files, items) {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +68,7 @@ export function scanFiles(dt) {
 | 
			
		|||
      } else if (entry.isDirectory) {
 | 
			
		||||
        const dir = {
 | 
			
		||||
          isDir: true,
 | 
			
		||||
          size: 0,
 | 
			
		||||
          path: `${directory}${entry.name}`
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,67 +100,34 @@ export function scanFiles(dt) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export function handleFiles(files, path, overwrite = false) {
 | 
			
		||||
  if (store.state.upload.count == 0) {
 | 
			
		||||
    buttons.loading('upload')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let promises = []
 | 
			
		||||
 | 
			
		||||
  let onupload = (id) => (event) => {
 | 
			
		||||
    store.commit('upload/setProgress', { id, loaded: event.loaded })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < files.length; i++) {
 | 
			
		||||
    let file = files[i]
 | 
			
		||||
 | 
			
		||||
    if (!file.isDir) {
 | 
			
		||||
      let filename = (file.fullPath !== undefined) ? file.fullPath : file.name
 | 
			
		||||
      let filenameEncoded = url.encodeRFC5987ValueChars(filename)
 | 
			
		||||
    let filename = (file.fullPath !== undefined) ? file.fullPath : file.name
 | 
			
		||||
    let filenameEncoded = url.encodeRFC5987ValueChars(filename)
 | 
			
		||||
 | 
			
		||||
      let id = store.state.upload.id
 | 
			
		||||
    let id = store.state.upload.id
 | 
			
		||||
 | 
			
		||||
      store.commit('upload/incrementSize', file.size)
 | 
			
		||||
      store.commit('upload/incrementId')
 | 
			
		||||
      store.commit('upload/incrementCount')
 | 
			
		||||
    let itemPath = path + filenameEncoded
 | 
			
		||||
 | 
			
		||||
      let promise = api.post(path + filenameEncoded, file, overwrite, throttle(onupload(id), 100)).finally(() => {
 | 
			
		||||
        store.commit('upload/decreaseCount')
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      promises.push(promise)
 | 
			
		||||
    } else {
 | 
			
		||||
      let uri = path
 | 
			
		||||
    if (file.isDir) {
 | 
			
		||||
      itemPath = path
 | 
			
		||||
      let folders = file.path.split("/")
 | 
			
		||||
 | 
			
		||||
      for (let i = 0; i < folders.length; i++) {
 | 
			
		||||
        let folder = folders[i]
 | 
			
		||||
        let folderEncoded = encodeURIComponent(folder)
 | 
			
		||||
        uri += folderEncoded + "/"
 | 
			
		||||
        itemPath += folderEncoded + "/"
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      api.post(uri)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let finish = () => {
 | 
			
		||||
    if (store.state.upload.count > 0) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buttons.success('upload')
 | 
			
		||||
    const item = {
 | 
			
		||||
      id,
 | 
			
		||||
      path: itemPath,
 | 
			
		||||
      file,
 | 
			
		||||
      overwrite
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    store.commit('setReload', true)
 | 
			
		||||
    store.commit('upload/reset')
 | 
			
		||||
    store.dispatch('upload/upload', item);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Promise.all(promises)
 | 
			
		||||
    .then(() => {
 | 
			
		||||
      finish()
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => {
 | 
			
		||||
      finish()
 | 
			
		||||
      this.$showError(error)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
  return false
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue