UNPKG

6.32 kBJavaScriptView Raw
1import io from 'socket.io-client';
2
3class SubWorker {
4 constructor({ onChange, ...params}, file) {
5 const defaultParams = {
6 chunkSize: 1 * 1024 * 1024,
7 maxConnectionAttempts: 10,
8 fileThrottle: 1000,
9 url: 'ws://localhost:5000/upload',
10 events: {
11 GET_LAST_CHUNK: 'get-last-chunk',
12 SEND_NEXT_CHUNK: 'send-next-chunk',
13 SEND_NEXT_CHUNK_SUCCESS: 'send-next-chunk-successful',
14 SEND_FILE_SUCCESS: 'send-file-successful',
15 CANCEL_UPLOAD: 'cancel-upload',
16 SEND_CHUNK_AGAIN: 'send-chunk-again',
17 ERROR: 'error'
18 }
19 };
20
21 this.onMessage = onChange;
22 this.socket = null;
23 this.file = file;
24 this.params = {...defaultParams, ...params};
25 this.events = this.params.events;
26 this.fileSize = this.file.data.size;
27 this.chunkSize = Math.min(this.params.chunkSize, this.fileSize);
28 this.maxChunk = (~~(this.fileSize / this.chunkSize)) - 1;
29 this.offset = 0;
30 this.start = 0;
31 this.end = this.chunkSize;
32 this.progress = 0;
33 this.maxConnectionAttempts = this.params.maxConnectionAttempts;
34 this.errorCount = 1;
35 this.throttle = this.params.fileThrottle;
36 this.previousUpdateTime = Date.now();
37 this.isSuspended = false;
38 this.updateFileState(false);
39 this.openSocket();
40 }
41
42 postMessage = (message) => {
43 this[message.event](message.payload);
44 }
45
46 pause = () => {
47 this.isSuspended = true;
48 }
49
50 resume = () => {
51 this.isSuspended = false;
52 this.process();
53 }
54
55 stop = () => {
56 this.onMessage({
57 data: {
58 payload: this.createFileObject(false),
59 event: 'cancelFileSender'
60 }
61 });
62 this.socket.emit(this.events.CANCEL_UPLOAD, this.file.id);
63 }
64
65 updateFileState = () => {
66 const now = Date.now();
67
68 if ((now - this.previousUpdateTime) >= this.throttle) {
69 this.previousUpdateTime = now;
70
71 this.onMessage({
72 data: {
73 payload: this.createFileObject(false),
74 event: 'onProgress'
75 }
76 });
77 }
78 }
79
80 closeFileSender = () => {
81 this.onMessage({
82 data: {
83 payload: this.createFileObject(true),
84 event: 'closeFileSender'
85 }
86 });
87 }
88
89 handleErrorMessage = (error) => {
90 this.onMessage({
91 data: {
92 payload: error,
93 event: 'onError'
94 }
95 });
96 }
97
98 createFileObject = (status) => {
99 return {
100 fileId: this.file.id,
101 name: this.file.data.name,
102 size: this.file.data.size,
103 passedBytes: this.end,
104 progress: this.progress,
105 currentChunk: this.offset,
106 type: this.file.data.type,
107 isFinal: status
108 };
109 }
110
111 process = () => {
112 const reader = new FileReaderSync();
113
114 this.start = this.offset * this.chunkSize;
115 this.end = Math.min(this.fileSize, (this.offset + 1) * this.chunkSize);
116
117 if (this.fileSize - this.end < this.chunkSize) {
118 this.end = this.fileSize;
119 }
120
121 const blob = this.slice(this.file.data, this.start, this.end);
122 const dataUrl = reader.readAsArrayBuffer(blob);
123 const final = this.offset === this.maxChunk;
124
125 const post = {
126 chunk: dataUrl,
127 fileId: this.file.id,
128 chunkNum: this.offset,
129 chunkSize: dataUrl.byteLength,
130 type: this.file.data.type,
131 name: this.file.data.name,
132 isFinal: final
133 };
134
135 this.socket.emit(this.events.SEND_NEXT_CHUNK, post);
136 this.previousSendTime = Date.now();
137 }
138
139 slice = (file, start, end) => {
140 const slice = file.mozSlice ||
141 file.webkitSlice ||
142 file.slice;
143
144 return slice.bind(file)(start, end);
145 }
146
147 openSocket = () => {
148 this.socket = io(this.params.url, { transports: ['websocket'] });
149
150 this.socket.on('connect', () => {
151 console.log('Socket ID: ' + this.socket.id + ' CONNECTED');
152
153 this.socket.emit(this.events.GET_LAST_CHUNK, JSON.stringify({id: this.file.id}));
154 });
155
156 this.socket.on(this.events.GET_LAST_CHUNK, (data) => {
157 this.offset = data;
158 this.updateFileState();
159 this.process();
160 });
161
162 this.socket.on(this.events.SEND_NEXT_CHUNK_SUCCESS, (event) => {
163 this.offset += 1;
164 this.progress = (this.offset / this.maxChunk) * 100;
165 this.updateFileState();
166
167 if (!this.isSuspended) {
168 this.process();
169 }
170 });
171
172 this.socket.on(this.events.SEND_FILE_SUCCESS, (event) => {
173 this.progress = this.offset > 0 ? (this.offset / this.maxChunk) * 100 : 100;
174 this.closeFileSender();
175 });
176
177 this.socket.on(this.events.SEND_CHUNK_AGAIN, () => {
178 this.process();
179 });
180
181 this.socket.on('disconnect', (reason) => {
182 if (reason === 'io server disconnect') {
183 console.log('Connection closed by server');
184 }
185
186 if (reason === 'transport close') {
187 if (this.errorCount <= this.maxConnectionAttempts) {
188 this.handleErrorMessage({
189 identifier: this.file.id,
190 error: {
191 message: 'disconnect',
192 reason: reason
193 }
194 });
195
196 console.log(this.errorCount + ' attempts - ' + 'Server Crashed');
197 this.errorCount += 1;
198 this.socket.open();
199 } else {
200 this.socket.disconnect(true);
201 console.log('Maximum reconnection attempts');
202 }
203 }
204
205 console.log('Connection closed, reason: ' + reason);
206 });
207
208 this.socket.on('connect_error', (reason) => {
209 console.log(reason);
210 this.handleErrorMessage({
211 identifier: this.file.id,
212 error: {
213 message: 'connect_error',
214 reason: ''
215 }
216 });
217
218 if (this.errorCount <= this.maxConnectionAttempts) {
219 console.log('Server is not responding or unavailable');
220 this.errorCount += 1;
221 } else {
222 this.socket.disconnect(true);
223 console.log('Maximum reconnection attempts');
224 }
225 });
226
227 this.socket.on('connect_failed', () => {
228 this.handleErrorMessage({
229 identifier: this.file.id,
230 error: {
231 message: 'connect_failed',
232 reason: ''
233 }
234 });
235
236 console.log('Connection Failed');
237 });
238
239 this.socket.on(this.events.ERROR, (error) => {
240 this.handleErrorMessage({
241 identifier: this.file.id,
242 error: error
243 });
244 });
245 }
246}
247
248export default SubWorker;