diff --git a/client/api/reader.js b/client/api/reader.js
index 91cb1e98..1088cf26 100644
--- a/client/api/reader.js
+++ b/client/api/reader.js
@@ -1,6 +1,7 @@
import axios from 'axios';
import {sleep} from '../share/utils';
+const maxFileUploadSize = 10*1024*1024;
const api = axios.create({
baseURL: '/api/reader'
});
@@ -65,6 +66,36 @@ class Reader {
//загрузка
return await axios.get(url, options);
}
+
+ async uploadFile(file, callback) {
+ if (file.size > maxFileUploadSize)
+ throw new Error(`Размер файла превышает ${maxFileUploadSize} байт`);
+
+ let formData = new FormData();
+ formData.append('file', file);
+
+ const options = {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ },
+ onUploadProgress: progress => {
+ const total = (progress.total ? progress.total : progress.loaded + 200000);
+ if (callback)
+ callback({state: 'upload', progress: Math.round((progress.loaded*100)/total)});
+ }
+
+ };
+
+ let response = await api.post('/upload-file', formData, options);
+ if (response.data.state == 'error')
+ throw new Error(response.data.error);
+
+ const url = response.data.url;
+ if (!url)
+ throw new Error('Неверный ответ api');
+
+ return url;
+ }
}
export default new Reader();
\ No newline at end of file
diff --git a/client/components/Reader/LoaderPage/LoaderPage.vue b/client/components/Reader/LoaderPage/LoaderPage.vue
index 75e7ddb9..923bf1a7 100644
--- a/client/components/Reader/LoaderPage/LoaderPage.vue
+++ b/client/components/Reader/LoaderPage/LoaderPage.vue
@@ -10,7 +10,8 @@
-
+
+
Загрузить файл с диска
@@ -66,7 +67,13 @@ class LoaderPage extends Vue {
}
}
- loadFle() {
+ loadFileClick() {
+ this.$refs.file.click();
+ }
+
+ loadFile() {
+ const file = this.$refs.file.files[0];
+ this.$emit('load-file', {file});
}
openHelp() {
diff --git a/client/components/Reader/ProgressPage/ProgressPage.vue b/client/components/Reader/ProgressPage/ProgressPage.vue
index cae5a345..7d1e9cb1 100644
--- a/client/components/Reader/ProgressPage/ProgressPage.vue
+++ b/client/components/Reader/ProgressPage/ProgressPage.vue
@@ -21,6 +21,7 @@ const ruMessage = {
'convert': 'конвертирование',
'loading': 'загрузка',
'parse': 'обработка',
+ 'upload': 'отправка',
};
export default @Component({
diff --git a/client/components/Reader/Reader.vue b/client/components/Reader/Reader.vue
index a45ad777..585e027e 100644
--- a/client/components/Reader/Reader.vue
+++ b/client/components/Reader/Reader.vue
@@ -50,6 +50,7 @@
{
+ const progress = this.$refs.page;
+ try {
+ progress.show();
+ progress.setState({state: 'upload'});
+
+ const url = await readerApi.uploadFile(opts.file, (state) => {
+ progress.setState(state);
+ });
+
+ this.loadBook(url);
+
+ progress.hide(); this.progressActive = false;
+ } catch (e) {
+ progress.hide(); this.progressActive = false;
+ this.loaderActive = true;
+ this.$alert(e.message, 'Ошибка', {type: 'error'});
+ }
+ });
+ }
+
blinkCachedLoadMessage() {
this.blinkCount = 30;
if (!this.inBlink) {
diff --git a/package-lock.json b/package-lock.json
index 8098ff27..698fbd36 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -619,6 +619,11 @@
"normalize-path": "^2.1.1"
}
},
+ "append-field": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+ "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
+ },
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -1840,8 +1845,7 @@
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
- "dev": true
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"buffer-xor": {
"version": "1.0.3",
@@ -1855,6 +1859,38 @@
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
"dev": true
},
+ "busboy": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+ "requires": {
+ "dicer": "0.2.5",
+ "readable-stream": "1.1.x"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ }
+ }
+ },
"byline": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz",
@@ -2379,7 +2415,6 @@
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "dev": true,
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
@@ -3251,6 +3286,38 @@
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
+ "dicer": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+ "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+ "requires": {
+ "readable-stream": "1.1.x",
+ "streamsearch": "0.1.2"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ }
+ }
+ },
"diffie-hellman": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@@ -6521,6 +6588,21 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
+ "multer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.1.tgz",
+ "integrity": "sha512-zzOLNRxzszwd+61JFuAo0fxdQfvku12aNJgnla0AQ+hHxFmfc/B7jBVuPr5Rmvu46Jze/iJrFpSOsD7afO8SDw==",
+ "requires": {
+ "append-field": "^1.0.0",
+ "busboy": "^0.2.11",
+ "concat-stream": "^1.5.2",
+ "mkdirp": "^0.5.1",
+ "object-assign": "^4.1.1",
+ "on-finished": "^2.3.0",
+ "type-is": "^1.6.4",
+ "xtend": "^4.0.0"
+ }
+ },
"multistream": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/multistream/-/multistream-2.1.1.tgz",
@@ -10405,6 +10487,11 @@
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
"dev": true
},
+ "streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+ },
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@@ -11036,8 +11123,7 @@
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
- "dev": true
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"uglify-js": {
"version": "3.4.9",
diff --git a/package.json b/package.json
index 81a98c46..379f402d 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,7 @@
"iconv-lite": "^0.4.24",
"localforage": "^1.7.3",
"lodash": "^4.17.11",
+ "multer": "^1.4.1",
"path-browserify": "^1.0.0",
"sql-template-strings": "^2.2.2",
"sqlite": "^3.0.0",
diff --git a/server/config/base.js b/server/config/base.js
index d27eb37f..0670e60b 100644
--- a/server/config/base.js
+++ b/server/config/base.js
@@ -13,6 +13,7 @@ module.exports = {
tempDir: `${dataDir}/tmp`,
logDir: `${dataDir}/log`,
publicDir: `${execDir}/public`,
+ uploadDir: `${execDir}/public/upload`,
dbFileName: 'db.sqlite',
loggingEnabled: true,
diff --git a/server/config/production.js b/server/config/production.js
index f9067cf4..98c40e72 100644
--- a/server/config/production.js
+++ b/server/config/production.js
@@ -10,6 +10,7 @@ module.exports = Object.assign({}, base, {
tempDir: `${dataDir}/tmp`,
logDir: `${dataDir}/log`,
publicDir: `${execDir}/public`,
+ uploadDir: `${execDir}/public/upload`,
servers: [
{
diff --git a/server/controllers/ReaderController.js b/server/controllers/ReaderController.js
index fecc9cc1..ae92f3a2 100644
--- a/server/controllers/ReaderController.js
+++ b/server/controllers/ReaderController.js
@@ -14,19 +14,25 @@ class ReaderController extends BaseController {
const request = req.body;
let error = '';
try {
- if (!request.type || !(request.type == 'url' || request.type == 'file'))
- throw new Error(`key 'type' is wrong`);
+ if (!request.url)
+ throw new Error(`key 'url' is empty`);
+ const workerId = this.readerWorker.loadBookUrl(request.url);
+ const state = workerState.getState(workerId);
+ return (state ? state : {});
+ } catch (e) {
+ error = e.message;
+ }
+ //bad request
+ res.status(400).send({error});
+ return false;
+ }
- if (request.type == 'file')
- throw new Error(`file loading is not supported yet`);
-
- if (request.type == 'url') {
- if (!request.url)
- throw new Error(`key 'url' is empty`);
- const workerId = this.readerWorker.loadBookUrl(request.url);
- const state = workerState.getState(workerId);
- return (state ? state : {});
- }
+ async uploadFile(req, res) {
+ const file = req.file;
+ let error = '';
+ try {
+ const url = await this.readerWorker.saveFile(file);
+ return ({url});
} catch (e) {
error = e.message;
}
diff --git a/server/core/ReaderWorker.js b/server/core/ReaderWorker.js
index ffccbe43..b90c64ed 100644
--- a/server/core/ReaderWorker.js
+++ b/server/core/ReaderWorker.js
@@ -88,6 +88,10 @@ class ReaderWorker {
return workerId;
}
+
+ async saveFile(file) {
+ return `file://${file.filename}`;
+ }
}
module.exports = ReaderWorker;
\ No newline at end of file
diff --git a/server/index.js b/server/index.js
index 4e655661..ff7157a2 100644
--- a/server/index.js
+++ b/server/index.js
@@ -13,6 +13,7 @@ const SqliteConnectionPool = require('./core/SqliteConnectionPool');
async function init() {
await fs.ensureDir(config.dataDir);
+ await fs.ensureDir(config.uploadDir);
await fs.ensureDir(config.tempDir);
await fs.emptyDir(config.tempDir);
}
diff --git a/server/routes.js b/server/routes.js
index 7bb412d5..1bee3b3a 100644
--- a/server/routes.js
+++ b/server/routes.js
@@ -1,4 +1,6 @@
const c = require('./controllers');
+const utils = require('./core/utils');
+const multer = require('multer');
function initRoutes(app, connPool, config) {
const misc = new c.MiscController(connPool, config);
@@ -9,16 +11,35 @@ function initRoutes(app, connPool, config) {
const [aAll, aNormal, aSite, aReader, aOmnireader] = // eslint-disable-line no-unused-vars
[config.mode, 'normal', 'site', 'reader', 'omnireader'];
+ //multer
+ const storage = multer.diskStorage({
+ destination: (req, file, cb) => {
+ cb(null, config.uploadDir);
+ },
+ filename: (req, file, cb) => {
+ cb(null, utils.randomHexString(30));
+ }
+ });
+ const upload = multer({ storage, limits: {fileSize: 10*1024*1024} });
+
//routes
const routes = [
['POST', '/api/config', misc.getConfig.bind(misc), [aAll], {}],
['POST', '/api/reader/load-book', reader.loadBook.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], {}],
];
//to app
for (let route of routes) {
- let [httpMethod, path, controller, access, options] = route;
+ let callbacks = [];
+ let [httpMethod, path, controllers, access, options] = route;
+ let controller = controllers;
+ if (Array.isArray(controllers)) {
+ controller = controllers[controllers.length - 1];
+ callbacks = controllers.slice(0, -1);
+ }
+
access = new Set(access);
let callback = () => {};
@@ -38,13 +59,14 @@ function initRoutes(app, connPool, config) {
res.status(403);
};
}
+ callbacks.push(callback);
switch (httpMethod) {
case 'GET' :
- app.get(path, callback);
+ app.get(path, ...callbacks);
break;
case 'POST':
- app.post(path, callback);
+ app.post(path, ...callbacks);
break;
default:
throw new Error(`initRoutes error: unknown httpMethod: ${httpMethod}`);