reader.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import axios from 'axios';
  2. import * as utils from '../share/utils';
  3. import * as cryptoUtils from '../share/cryptoUtils';
  4. import wsc from './webSocketConnection';
  5. const api = axios.create({
  6. baseURL: '/api/reader'
  7. });
  8. const workerApi = axios.create({
  9. baseURL: '/api/worker'
  10. });
  11. class Reader {
  12. constructor() {
  13. }
  14. async getWorkerStateFinish(workerId, callback) {
  15. if (!callback) callback = () => {};
  16. let response = {};
  17. try {
  18. const requestId = await wsc.send({action: 'worker-get-state-finish', workerId});
  19. let prevResponse = false;
  20. while (1) {// eslint-disable-line no-constant-condition
  21. response = await wsc.message(requestId);
  22. if (!response.state && prevResponse !== false) {//экономия траффика
  23. callback(prevResponse);
  24. } else {//были изменения worker state
  25. if (!response.state)
  26. throw new Error('Неверный ответ api');
  27. callback(response);
  28. prevResponse = response;
  29. }
  30. if (response.state == 'finish' || response.state == 'error') {
  31. break;
  32. }
  33. }
  34. return response;
  35. } catch (e) {
  36. console.error(e);
  37. }
  38. //если с WebSocket проблема, работаем по http
  39. const refreshPause = 500;
  40. let i = 0;
  41. response = {};
  42. while (1) {// eslint-disable-line no-constant-condition
  43. const prevProgress = response.progress || 0;
  44. const prevState = response.state || 0;
  45. response = await workerApi.post('/get-state', {workerId});
  46. response = response.data;
  47. callback(response);
  48. if (!response.state)
  49. throw new Error('Неверный ответ api');
  50. if (response.state == 'finish' || response.state == 'error') {
  51. break;
  52. }
  53. if (i > 0)
  54. await utils.sleep(refreshPause);
  55. i++;
  56. if (i > 180*1000/refreshPause) {//3 мин ждем телодвижений воркера
  57. throw new Error('Слишком долгое время ожидания');
  58. }
  59. //проверка воркера
  60. i = (prevProgress != response.progress || prevState != response.state ? 1 : i);
  61. }
  62. return response;
  63. }
  64. async loadBook(opts, callback) {
  65. if (!callback) callback = () => {};
  66. let response = await api.post('/load-book', opts);
  67. const workerId = response.data.workerId;
  68. if (!workerId)
  69. throw new Error('Неверный ответ api');
  70. callback({totalSteps: 4});
  71. callback(response.data);
  72. response = await this.getWorkerStateFinish(workerId, callback);
  73. if (response) {
  74. if (response.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл
  75. callback({step: 4});
  76. const book = await this.loadCachedBook(response.path, callback, response.size);
  77. return Object.assign({}, response, {data: book.data});
  78. }
  79. if (response.state == 'error') {
  80. let errMes = response.error;
  81. if (errMes.indexOf('getaddrinfo') >= 0 ||
  82. errMes.indexOf('ECONNRESET') >= 0 ||
  83. errMes.indexOf('EINVAL') >= 0 ||
  84. errMes.indexOf('404') >= 0)
  85. errMes = `Ресурс не найден по адресу: ${response.url}`;
  86. throw new Error(errMes);
  87. }
  88. } else {
  89. throw new Error('Пустой ответ сервера');
  90. }
  91. }
  92. async checkCachedBook(url) {
  93. let estSize = -1;
  94. try {
  95. const response = await axios.head(url, {headers: {'Cache-Control': 'no-cache'}});
  96. if (response.headers['content-length']) {
  97. estSize = response.headers['content-length'];
  98. }
  99. } catch (e) {
  100. //
  101. }
  102. return estSize;
  103. }
  104. async loadCachedBook(url, callback, estSize = -1) {
  105. if (!callback) callback = () => {};
  106. callback({state: 'loading', progress: 0});
  107. //получение размера файла
  108. if (estSize && estSize < 0) {
  109. estSize = await this.checkCachedBook(url);
  110. }
  111. //получение файла
  112. estSize = (estSize > 0 ? estSize : 1000000);
  113. const options = {
  114. onDownloadProgress: (progress) => {
  115. while (progress.loaded > estSize) estSize *= 1.5;
  116. if (callback)
  117. callback({progress: Math.round((progress.loaded*100)/estSize)});
  118. }
  119. }
  120. return await axios.get(url, options);
  121. }
  122. async uploadFile(file, maxUploadFileSize = 10*1024*1024, callback) {
  123. if (file.size > maxUploadFileSize)
  124. throw new Error(`Размер файла превышает ${maxUploadFileSize} байт`);
  125. let formData = new FormData();
  126. formData.append('file', file, file.name);
  127. const options = {
  128. headers: {
  129. 'Content-Type': 'multipart/form-data'
  130. },
  131. onUploadProgress: progress => {
  132. const total = (progress.total ? progress.total : progress.loaded + 200000);
  133. if (callback)
  134. callback({state: 'upload', progress: Math.round((progress.loaded*100)/total)});
  135. }
  136. };
  137. let response = await api.post('/upload-file', formData, options);
  138. if (response.data.state == 'error')
  139. throw new Error(response.data.error);
  140. const url = response.data.url;
  141. if (!url)
  142. throw new Error('Неверный ответ api');
  143. return url;
  144. }
  145. async storage(request) {
  146. let response = null;
  147. try {
  148. response = await wsc.message(await wsc.send({action: 'reader-storage', body: request}));
  149. } catch (e) {
  150. console.error(e);
  151. //если с WebSocket проблема, работаем по http
  152. response = await api.post('/storage', request);
  153. response = response.data;
  154. }
  155. const state = response.state;
  156. if (!state)
  157. throw new Error('Неверный ответ api');
  158. if (state == 'error') {
  159. throw new Error(response.error);
  160. }
  161. return response;
  162. }
  163. makeUrlFromBuf(buf) {
  164. const key = utils.toHex(cryptoUtils.sha256(buf));
  165. return `disk://${key}`;
  166. }
  167. async uploadFileBuf(buf, url) {
  168. if (!url)
  169. url = this.makeUrlFromBuf(buf);
  170. let response;
  171. try {
  172. await axios.head(url.replace('disk://', '/upload/'), {headers: {'Cache-Control': 'no-cache'}});
  173. response = await wsc.message(await wsc.send({action: 'upload-file-touch', url}));
  174. } catch (e) {
  175. response = await wsc.message(await wsc.send({action: 'upload-file-buf', buf}));
  176. }
  177. if (response.error)
  178. throw new Error(response.error);
  179. return response;
  180. }
  181. async getUploadedFileBuf(url) {
  182. url = url.replace('disk://', '/upload/');
  183. return (await axios.get(url)).data;
  184. }
  185. }
  186. export default new Reader();