redux 可通过 axios onProgress 观察

redux observable with axios onProgress

我正在创建一个上传功能,它将在 React Redux and Redux-observable, and i use axios 中向客户端显示进度条以向 AWS S3 发出放置请求。

我的epics如下

...

function uploadFile(mimetype, url, file) {
  const config = {
    headers: {
      'Content-Type': mimetype,
    },
    onUploadProgress(progress) {
      const percentCompleted = Math.round((progress.loaded * 100) / progress.total)
      uploadProgress(percentCompleted)
    },
  }

  axiosRetry(axios, { retries: 3 })

  return axios.put(url, file[0], config)
}

export const uploadEpic = (action$, store) => action$
  .ofType(SIGNED_URL_SUCCESS)
  .mergeMap(() => {
    const file = store.getState().File.droppedFile
    const mimetype = file[0].type
    const { url } = store.getState().SignedUrl
    const { fileData } = store.getState().Upload

    return of(uploadFile(mimetype, url.data, file))
      .concatMap(() => {
        const uploadedData = {
          url: fileData.url,
          thumbUrl: `${fileData.folder}/${fileData.filename}-00001.png`,
        }
        return [
          upload(uploadedData),
          uploadSuccess(),
        ]
      })
      .catch(error => of(uploadFailure(error)))
  })

export default uploadEpic

上传似乎有效,因为我收到一封 AWS SNS 电子邮件,告知它已完成,但我似乎看不到它正在更新我的上传减速器中的 Upload.progress 状态。

我使用 axios 的原因很特别,因为它的 axios-retryonUploadProgress,因为我似乎找不到使用 universal-rx-request[=15 执行 onProgress 的示例=]

大概有两个问题

  1. 我如何使用 axios 实现此目的
  2. 我如何使用 universal-rx-request
  3. 实现此目的

多亏了这个

我最终完全没有使用 axios

我用它工作了

import { of } from 'rxjs/observable/of'
import { Subject } from 'rxjs/Subject'

import { Observable } from 'rxjs/Observable'
import 'rxjs/add/observable/dom/ajax'

import { SIGNED_URL_SUCCESS } from 'ducks/SignedUrl'

import {
  upload,
  uploadIsLoading,
  uploadSuccess,
  uploadFailure,
  uploadProgress,
} from 'ducks/Upload'

export const uploadEpic = (action$, store) => action$
  .ofType(SIGNED_URL_SUCCESS)
  .mergeMap(() => {
    const file = store.getState().File.droppedFile
    const mimetype = file[0].type
    const { url } = store.getState().SignedUrl
    const { fileData } = store.getState().Upload

    const progressSubscriber = new Subject()
    const request = Observable.ajax({
      method: 'PUT',
      url: url.data,
      body: file[0],
      headers: {
        'Content-Type': mimetype,
      },
      progressSubscriber,
    })

    const requestObservable = request
      .concatMap(() => {
        const uploadedData = {
            ...
        }
        return [
          upload(uploadedData),
          uploadIsLoading(false),
          uploadSuccess(),
        ]
      })
      .catch(error => of(uploadFailure(error)))

    return progressSubscriber
      .map(e => ({ percentage: (e.loaded / e.total) * 100 }))
      .map(data => uploadProgress(data.percentage))
      .merge(requestObservable)
  })

更新: on rxjs 6 the merge operators is deprecated,因此如果您使用的是 rxjs 6,请将上面的代码更改为

// some/lib/folder/uploader.js
import { of, merge } from 'rxjs' // import merge here
import { ajax } from 'rxjs/ajax'
import { map, catchError } from 'rxjs/operators' // instead of here
import { Subject } from 'rxjs/Subject'


export function storageUploader(...args) {
  const progressSubscriber = new Subject()

  const request = ajax({...someRequestOptions})
    .pipe(
      map(() => success()),
      catchError((error) => of(failure(error))),
    )

  const subscriber = progressSubscriber
    .pipe(
      map((e) => ({ percentage: (e.loaded / e.total) * 100 })),
      map((upload) => progress(upload.percentage)),
      catchError((error) => of(failure(error))),
    )

  return merge(subscriber, request) // merge both like this, instead of chaining the request on progressSubscriber
}


//the_epic.js
export function uploadEpic(action$, state$) {
  return action$
    .pipe(
      ofType(UPLOAD),
      mergeMap((someUploadOptions) => uploaderLib(
        { ...someUploadOptions },
        actionSuccess,
        actionFailure,
        actionProgress,
      )),
      catchError((error) => of(actionFailure(error))),
    )
}