From 6faa7b2efe1b78a0ffffaa9223ee92e02cec7d9c Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Sun, 12 Jan 2020 18:51:12 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BC=D0=B5=D0=BD=D1=8C=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE?= =?UTF-8?q?=D0=B2=20get-state=20=D0=BA=20api,=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20get?= =?UTF-8?q?-state-finish?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/api/reader.js | 62 +++++++++++++++----------- server/controllers/WorkerController.js | 55 +++++++++++++++++++++++ server/routes.js | 1 + 3 files changed, 93 insertions(+), 25 deletions(-) diff --git a/client/api/reader.js b/client/api/reader.js index b3e2da87..4487119d 100644 --- a/client/api/reader.js +++ b/client/api/reader.js @@ -1,7 +1,5 @@ import axios from 'axios'; -import * as utils from '../share/utils'; - const api = axios.create({ baseURL: '/api/reader' }); @@ -12,7 +10,6 @@ const workerApi = axios.create({ class Reader { async loadBook(opts, callback) { - const refreshPause = 300; if (!callback) callback = () => {}; let response = await api.post('/load-book', opts); @@ -22,37 +19,52 @@ class Reader { throw new Error('Неверный ответ api'); callback({totalSteps: 4}); + callback(response.data); - let i = 0; - while (1) {// eslint-disable-line no-constant-condition - callback(response.data); + //присылается текст, состоящий из json-объектов state каждые 300ms, с разделителем splitter между ними + const splitter = '-- aod2t5hDXU32bUFyqlFE next status --'; + let lastIndex = 0; + response = await workerApi.post('/get-state-finish', {workerId}, { + onDownloadProgress: progress => { + //небольая оптимизация, вместо простого responseText.split + const xhr = progress.target; + let currIndex = xhr.responseText.length; + if (lastIndex == currIndex) + return; + const last = xhr.responseText.substring(lastIndex, currIndex); + lastIndex = currIndex; - if (response.data.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл - callback({step: 4}); - const book = await this.loadCachedBook(response.data.path, callback); - return Object.assign({}, response.data, {data: book.data}); + //быстрее будет last.split + const res = last.split(splitter).pop(); + if (res) { + callback(JSON.parse(res)); + } } - if (response.data.state == 'error') { - let errMes = response.data.error; + }); + + //берем последний state + response = response.data.split(splitter).pop(); + + if (response) { + response = JSON.parse(response); + + if (response.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл + callback({step: 4}); + const book = await this.loadCachedBook(response.path, callback); + return Object.assign({}, response, {data: book.data}); + } + + if (response.state == 'error') { + let errMes = response.error; if (errMes.indexOf('getaddrinfo') >= 0 || errMes.indexOf('ECONNRESET') >= 0 || errMes.indexOf('EINVAL') >= 0 || errMes.indexOf('404') >= 0) - errMes = `Ресурс не найден по адресу: ${response.data.url}`; + errMes = `Ресурс не найден по адресу: ${response.url}`; throw new Error(errMes); } - if (i > 0) - await utils.sleep(refreshPause); - - i++; - if (i > 120*1000/refreshPause) {//2 мин ждем телодвижений воркера - throw new Error('Слишком долгое время ожидания'); - } - //проверка воркера - const prevProgress = response.data.progress; - const prevState = response.data.state; - response = await workerApi.post('/get-state', {workerId}); - i = (prevProgress != response.data.progress || prevState != response.data.state ? 1 : i); + } else { + throw new Error('Пустой ответ сервера'); } } diff --git a/server/controllers/WorkerController.js b/server/controllers/WorkerController.js index 69bd4982..7fa2647f 100644 --- a/server/controllers/WorkerController.js +++ b/server/controllers/WorkerController.js @@ -1,5 +1,6 @@ const BaseController = require('./BaseController'); const WorkerState = require('../core/WorkerState');//singleton +const utils = require('../core/utils'); class WorkerController extends BaseController { constructor(config) { @@ -15,6 +16,7 @@ class WorkerController extends BaseController { throw new Error(`key 'workerId' is wrong`); const state = this.workerState.getState(request.workerId); + return (state ? state : {}); } catch (e) { error = e.message; @@ -23,6 +25,59 @@ class WorkerController extends BaseController { res.status(400).send({error}); return false; } + + async getStateFinish(req, res) { + const request = req.body; + let error = ''; + try { + if (!request.workerId) + throw new Error(`key 'workerId' is wrong`); + + res.writeHead(200, { + 'Content-Type': 'text/json; charset=utf-8', + }); + + const splitter = '-- aod2t5hDXU32bUFyqlFE next status --'; + const refreshPause = 300; + let i = 0; + let prevProgress = -1; + let prevState = ''; + let state; + while (1) {// eslint-disable-line no-constant-condition + state = this.workerState.getState(request.workerId); + if (!state) break; + + res.write(splitter + JSON.stringify(state)); + res.flush(); + + if (state.state != 'finish') + await utils.sleep(refreshPause); + else + break; + + i++; + if (i > 2*60*1000/refreshPause) {//2 мин ждем телодвижений воркера + res.write(splitter + JSON.stringify({state: 'error', error: 'Слишком долгое время ожидания'})); + break; + } + i = (prevProgress != state.progress || prevState != state.state ? 1 : i); + prevProgress = state.progress; + prevState = state.state; + } + + if (!state) { + res.write(splitter + JSON.stringify({})); + } + + res.end(); + return false; + } catch (e) { + error = e.message; + } + //bad request + res.status(400).send({error}); + return false; + } } module.exports = WorkerController; diff --git a/server/routes.js b/server/routes.js index ffdd92c8..08d20585 100644 --- a/server/routes.js +++ b/server/routes.js @@ -29,6 +29,7 @@ function initRoutes(app, config) { ['POST', '/api/reader/storage', reader.storage.bind(reader), [aAll], {}], ['POST', '/api/reader/upload-file', [upload.single('file'), reader.uploadFile.bind(reader)], [aAll], {}], ['POST', '/api/worker/get-state', worker.getState.bind(worker), [aAll], {}], + ['POST', '/api/worker/get-state-finish', worker.getStateFinish.bind(worker), [aAll], {}], ]; //to app