· 6 years ago · Feb 01, 2020, 09:52 PM
1// @flow
2import { call, put, select } from 'redux-saga/effects';
3import type { Saga } from 'redux-saga';
4import axios from 'axios';
5import { toast } from 'react-toastify';
6
7import * as actions from 'app/actions/file';
8import { iTokenStorage } from 'core/interfaces/TokenStorage';
9import type { UploadFileType } from 'app/types/file';
10import api from 'app/routes/api';
11import { getUploadedFileInfo } from 'app/selectors/file';
12import TokenStorage from 'core/services/TokenStorage';
13
14const CHUNK_FILE_SIZE = 90 * 1024 * 1024;
15
16// TODO: need to refactoring
17function onProgress(key, file, chunk, chunkNum) {
18 return function (progressEvent) {
19 const { loaded } = progressEvent;
20
21 import('core/utils/store')
22 .then(({ store }) => {
23 const { getState, dispatch } = store;
24
25 const uploadedFileInfo = getUploadedFileInfo(getState());
26
27 const prevState = uploadedFileInfo ? uploadedFileInfo[key] : {};
28
29 const realLoaded = chunk.size > loaded ? loaded : chunk.size;
30
31 dispatch(actions.setUploadedFileAction({
32 ...uploadedFileInfo,
33 [key]: {
34 ...prevState,
35 progress: Math.round(((chunkNum * CHUNK_FILE_SIZE + realLoaded) * 100) / file.size),
36 rez : null,
37 name : file.name
38 }
39 }));
40 });
41 };
42}
43
44export function* uploadFile({ payload }: UploadFileType): Saga<void> {
45 const tokenStorage: iTokenStorage = TokenStorage;
46
47 const formData = new FormData();
48
49 const { file: { file }, key } = payload;
50
51 const fileSize = file.size;
52 const chunksCount = Math.ceil(fileSize / CHUNK_FILE_SIZE);
53 let fileName = '';
54 let error = false;
55
56 for (let chunkNum = 0; chunkNum < chunksCount && !error; chunkNum++) {
57 const offset = chunkNum * CHUNK_FILE_SIZE;
58
59 const chunk = file.slice(offset, offset + CHUNK_FILE_SIZE, file.type);
60
61 formData.set('file', chunk);
62
63 if (fileName) {
64 formData.set('fileName', fileName);
65 }
66
67 if (!chunkNum) {
68 try {
69 // TODO: перписать HTTP service с fetch на axios
70 // flow-disable-next-line
71 const { data } = yield call(axios.post, `${ process.env.API_HOST }${ api.file.post.path }`, formData, {
72 headers: {
73 Authorization: `Bearer ${ tokenStorage.token || '' }`,
74 },
75 onUploadProgress: onProgress(key, file, chunk, chunkNum)
76 });
77
78 fileName = data.rez;
79
80 if (chunksCount === 1) {
81 const uploadedFileInfo = yield select(getUploadedFileInfo);
82
83 const prevState = uploadedFileInfo ? uploadedFileInfo[key] : {};
84
85 yield put(actions.setUploadedFileAction({
86 ...uploadedFileInfo,
87 [key]: {
88 ...prevState,
89 ...data,
90 name: file.name
91 }
92 }));
93
94 toast.success(`${ file.name } - Успешно был загружен на сервер`);
95 }
96 } catch (err) {
97 toast.error('Ошибка загрузки файла');
98
99 error = true;
100
101 window.console.log(err);
102 }
103 } else {
104 try {
105 // flow-disable-next-line
106 yield call(axios.patch, `${ process.env.API_HOST }${ api.file.post.path }`, formData, {
107 headers: {
108 Authorization: `Bearer ${ tokenStorage.token || '' }`,
109 },
110 onUploadProgress: onProgress(key, file, chunk, chunkNum)
111 });
112
113 if (chunkNum === chunksCount - 1) {
114 const uploadedFileInfo = yield select(getUploadedFileInfo);
115
116 const prevState = uploadedFileInfo ? uploadedFileInfo[key] : {};
117
118 yield put(actions.setUploadedFileAction({
119 ...uploadedFileInfo,
120 [key]: {
121 ...prevState,
122 rez : fileName,
123 name: file.name
124 }
125 }));
126
127 toast.success(`${ file.name } - Успешно был загружен на сервер`);
128 }
129 } catch (err) {
130 toast.error('Ошибка загрузки файла');
131
132 error = true;
133
134 window.console.log(err);
135 }
136 }
137 }
138}