diff --git a/LICENSE b/LICENSE
index 8363a1e..b9065e1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) Alibaba Group Holding Limited and other contributors.
+Copyright (c) 2017-present Alibaba Group Holding Limited and other contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index c274b7d..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-environment:
- matrix:
- - nodejs_version: '8'
-
-install:
- - ps: Install-Product node $env:nodejs_version
- - npm i npminstall && node_modules\.bin\npminstall
-
-test_script:
- - node --version
- - npm --version
- - npm run test
-
-build: off
diff --git a/bin/test.js b/bin/test.js
index 0f8b2da..d65f66f 100644
--- a/bin/test.js
+++ b/bin/test.js
@@ -39,7 +39,7 @@ class Test extends Command {
);
try {
const flag = argv.c ? ' -c' : '';
- await this.runscript(`npminstall${flag}`, { cwd: dir });
+ await this.runscript(`npmupdate${flag}`, { cwd: dir });
await this.runscript('npm test', { cwd: dir });
console.info('%s success\n', chalk.green('✔'));
success.add(dir);
diff --git a/multipart-file-mode/.gitignore b/multipart-file-mode/.gitignore
new file mode 100644
index 0000000..c2c9783
--- /dev/null
+++ b/multipart-file-mode/.gitignore
@@ -0,0 +1,3 @@
+upload_dirs
+!upload_dirs/keepit
+app/public
diff --git a/multipart-file-mode/app/controller/ajax.js b/multipart-file-mode/app/controller/ajax.js
new file mode 100644
index 0000000..69c3e9d
--- /dev/null
+++ b/multipart-file-mode/app/controller/ajax.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const fs = require('mz/fs');
+const path = require('path');
+const Controller = require('egg').Controller;
+const pump = require('mz-modules/pump');
+
+module.exports = class extends Controller {
+ async show() {
+ await this.ctx.render('page/ajax.html');
+ }
+
+ async upload() {
+ const { ctx } = this;
+ const file = ctx.request.files[0];
+ if (!file) return ctx.throw(404);
+
+ const filename = encodeURIComponent(ctx.request.body.name) + path.extname(file.filename).toLowerCase();
+ const targetPath = path.join(this.config.baseDir, 'app/public', filename);
+ const source = fs.createReadStream(file.filepath);
+ const target = fs.createWriteStream(targetPath);
+ await pump(source, target);
+ ctx.logger.warn('save %s to %s', file.filepath, targetPath);
+ // delete tmp file
+ await fs.unlink(file.filepath);
+
+ ctx.body = { url: '/public/' + filename };
+ }
+};
diff --git a/multipart-file-mode/app/controller/form.js b/multipart-file-mode/app/controller/form.js
new file mode 100644
index 0000000..4886a46
--- /dev/null
+++ b/multipart-file-mode/app/controller/form.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const fs = require('mz/fs');
+const path = require('path');
+const pump = require('mz-modules/pump');
+const Controller = require('egg').Controller;
+
+module.exports = class extends Controller {
+ async show() {
+ await this.ctx.render('page/form.html');
+ }
+
+ async upload() {
+ const { ctx } = this;
+ const file = ctx.request.files[0];
+ if (!file) return ctx.throw(404);
+
+ const filename = encodeURIComponent(ctx.request.body.name) + path.extname(file.filename).toLowerCase();
+ const targetPath = path.join(this.config.baseDir, 'app/public', filename);
+ const source = fs.createReadStream(file.filepath);
+ const target = fs.createWriteStream(targetPath);
+ await pump(source, target);
+ ctx.logger.warn('save %s to %s', file.filepath, targetPath);
+ // delete tmp file
+ await fs.unlink(file.filepath);
+
+ ctx.redirect('/public/' + filename);
+ }
+};
diff --git a/multipart-file-mode/app/controller/home.js b/multipart-file-mode/app/controller/home.js
new file mode 100644
index 0000000..3038487
--- /dev/null
+++ b/multipart-file-mode/app/controller/home.js
@@ -0,0 +1,9 @@
+'use strict';
+
+const Controller = require('egg').Controller;
+
+module.exports = class extends Controller {
+ async render() {
+ await this.ctx.render('index.html');
+ }
+};
diff --git a/multipart-file-mode/app/controller/multiple.js b/multipart-file-mode/app/controller/multiple.js
new file mode 100644
index 0000000..31ec0ef
--- /dev/null
+++ b/multipart-file-mode/app/controller/multiple.js
@@ -0,0 +1,41 @@
+'use strict';
+
+const fs = require('mz/fs');
+const path = require('path');
+const pump = require('mz-modules/pump');
+const Controller = require('egg').Controller;
+
+module.exports = class extends Controller {
+ async show() {
+ await this.ctx.render('page/multiple.html');
+ }
+
+ async upload() {
+ const { ctx } = this;
+ const files = ctx.request.files;
+ ctx.logger.warn('files: %j', files);
+ for (const file of files) {
+ const filename = file.filename.toLowerCase();
+ const targetPath = path.join(this.config.baseDir, 'app/public', filename);
+ const source = fs.createReadStream(file.filepath);
+ const target = fs.createWriteStream(targetPath);
+ await pump(source, target);
+ ctx.logger.warn('save %s to %s', file.filepath, targetPath);
+ // delete tmp file
+ await fs.unlink(file.filepath);
+ }
+
+ const fields = [];
+ for (const k in ctx.request.body) {
+ fields.push({
+ key: k,
+ value: ctx.request.body[k],
+ });
+ }
+
+ await ctx.render('page/multiple_result.html', {
+ fields,
+ files,
+ });
+ }
+};
diff --git a/multipart-file-mode/app/router.js b/multipart-file-mode/app/router.js
new file mode 100644
index 0000000..9b7e401
--- /dev/null
+++ b/multipart-file-mode/app/router.js
@@ -0,0 +1,14 @@
+'use strict';
+
+module.exports = app => {
+ app.router.get('/', 'home.render');
+
+ app.router.get('/ajax', app.controller.ajax.show);
+ app.router.post('/ajax', app.controller.ajax.upload);
+
+ app.router.get('/form', app.controller.form.show);
+ app.router.post('/form', app.controller.form.upload);
+
+ app.router.get('/multiple-file', app.controller.multiple.show);
+ app.router.post('/multiple-file', app.controller.multiple.upload);
+};
diff --git a/multipart-file-mode/app/view/index.html b/multipart-file-mode/app/view/index.html
new file mode 100644
index 0000000..15799e5
--- /dev/null
+++ b/multipart-file-mode/app/view/index.html
@@ -0,0 +1,9 @@
+{% extends "layout.html" %}
+{% block content %}
+
Upload
+
+{% endblock %}
diff --git a/multipart-file-mode/app/view/layout.html b/multipart-file-mode/app/view/layout.html
new file mode 100644
index 0000000..a016e35
--- /dev/null
+++ b/multipart-file-mode/app/view/layout.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+{% block content %}
+{% endblock %}
+
+
diff --git a/multipart-file-mode/app/view/page/ajax.html b/multipart-file-mode/app/view/page/ajax.html
new file mode 100644
index 0000000..0187141
--- /dev/null
+++ b/multipart-file-mode/app/view/page/ajax.html
@@ -0,0 +1,43 @@
+{% extends "layout.html" %}
+
+{% block content %}
+ Upload Image
+
+
+
+{% endblock %}
diff --git a/multipart-file-mode/app/view/page/form.html b/multipart-file-mode/app/view/page/form.html
new file mode 100644
index 0000000..1dee9c2
--- /dev/null
+++ b/multipart-file-mode/app/view/page/form.html
@@ -0,0 +1,12 @@
+{% extends "layout.html" %}
+
+{% block content %}
+ Upload Image
+
+{% endblock %}
diff --git a/multipart-file-mode/app/view/page/multiple.html b/multipart-file-mode/app/view/page/multiple.html
new file mode 100644
index 0000000..19c157a
--- /dev/null
+++ b/multipart-file-mode/app/view/page/multiple.html
@@ -0,0 +1,14 @@
+{% extends "layout.html" %}
+{% block content %}
+ Upload Image
+
+{% endblock %}
diff --git a/multipart-file-mode/app/view/page/multiple_result.html b/multipart-file-mode/app/view/page/multiple_result.html
new file mode 100644
index 0000000..24278ce
--- /dev/null
+++ b/multipart-file-mode/app/view/page/multiple_result.html
@@ -0,0 +1,13 @@
+{% extends "layout.html" %}
+{% block content %}
+ Upload Result
+
+ {% for field in fields %}
+ - {{ field.key }}: {{ field.value }}
+ {% endfor %}
+
+ {% for file in files %}
+ - {{ file.filename }}
+ {% endfor %}
+
+{% endblock %}
diff --git a/multipart-file-mode/config/config.default.js b/multipart-file-mode/config/config.default.js
new file mode 100644
index 0000000..bfc6e31
--- /dev/null
+++ b/multipart-file-mode/config/config.default.js
@@ -0,0 +1,11 @@
+'use strict';
+
+exports.keys = 'my keys';
+
+exports.view = {
+ defaultViewEngine: 'nunjucks',
+};
+
+exports.multipart = {
+ mode: 'file',
+};
diff --git a/multipart-file-mode/config/plugin.js b/multipart-file-mode/config/plugin.js
new file mode 100644
index 0000000..1edbc89
--- /dev/null
+++ b/multipart-file-mode/config/plugin.js
@@ -0,0 +1,6 @@
+'use strict';
+
+exports.nunjucks = {
+ enable: true,
+ package: 'egg-view-nunjucks',
+};
diff --git a/multipart-file-mode/package.json b/multipart-file-mode/package.json
new file mode 100644
index 0000000..1d86014
--- /dev/null
+++ b/multipart-file-mode/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "multipart-file-mode-example",
+ "dependencies": {
+ "egg": "^2.11.2",
+ "egg-view-nunjucks": "^2.1.4",
+ "mz": "^2.7.0",
+ "mz-modules": "^2.1.0"
+ },
+ "devDependencies": {
+ "egg-bin": "^4.3.5",
+ "egg-mock": "^3.13.1"
+ },
+ "scripts": {
+ "dev": "egg-bin dev",
+ "test": "egg-bin test",
+ "cov": "egg-bin cov"
+ },
+ "private": true
+}
diff --git a/multipart-file-mode/test/index.test.js b/multipart-file-mode/test/index.test.js
new file mode 100644
index 0000000..b7378be
--- /dev/null
+++ b/multipart-file-mode/test/index.test.js
@@ -0,0 +1,83 @@
+'use strict';
+
+const path = require('path');
+const assert = require('assert');
+const mm = require('egg-mock');
+const rimraf = require('mz-modules/rimraf');
+
+describe('example multipart test', () => {
+ let app;
+ before(async () => {
+ app = mm.app();
+ await app.ready();
+ });
+ after(() => app.close());
+ after(() => rimraf(path.join(app.config.baseDir, 'app/public')));
+
+ describe('form', () => {
+ it('should upload', async () => {
+ app.mockCsrf();
+ await app.httpRequest()
+ .post('/form')
+ .field('name', 'form')
+ .attach('file', path.join(__dirname, 'mc.jpeg'))
+ .expect('Location', '/public/form.jpeg')
+ .expect(302);
+
+ await app.httpRequest()
+ .get('/public/form.jpeg')
+ .expect('content-length', '6635')
+ .expect(200);
+ });
+ });
+
+ describe('multiple', () => {
+ it('should upload', async () => {
+ app.mockCsrf();
+ await app.httpRequest()
+ .post('/multiple-file')
+ .field('name1', '1')
+ .attach('file1', path.join(__dirname, 'mc.jpeg'))
+ .field('name2', '2')
+ .attach('file2', path.join(__dirname, 'kfc.jpeg'))
+ .field('name3', '3')
+ .expect(res => {
+ assert(res.text.includes('name1: 1'));
+ assert(res.text.includes('name2: 2'));
+ assert(res.text.includes('name3: 3'));
+ assert(res.text.includes('href="/public/mc.jpeg"'));
+ assert(res.text.includes('href="/public/kfc.jpeg"'));
+ })
+ .expect(200);
+
+ await app.httpRequest()
+ .get('/public/mc.jpeg')
+ .expect('content-length', '6635')
+ .expect(200);
+
+ await app.httpRequest()
+ .get('/public/kfc.jpeg')
+ .expect('content-length', '28810')
+ .expect(200);
+ });
+ });
+
+ describe('ajax', () => {
+ it('should upload', async () => {
+ app.mockCsrf();
+ await app.httpRequest()
+ .post('/ajax')
+ .field('name', 'ajax')
+ .attach('file', path.join(__dirname, 'mc.jpeg'))
+ .expect({
+ url: '/public/ajax.jpeg',
+ })
+ .expect(200);
+
+ await app.httpRequest()
+ .get('/public/ajax.jpeg')
+ .expect('content-length', '6635')
+ .expect(200);
+ });
+ });
+});
diff --git a/multipart-file-mode/test/kfc.jpeg b/multipart-file-mode/test/kfc.jpeg
new file mode 100644
index 0000000..8a43190
Binary files /dev/null and b/multipart-file-mode/test/kfc.jpeg differ
diff --git a/multipart-file-mode/test/mc.jpeg b/multipart-file-mode/test/mc.jpeg
new file mode 100644
index 0000000..a1bedce
Binary files /dev/null and b/multipart-file-mode/test/mc.jpeg differ
diff --git a/package.json b/package.json
index 20b0814..eb04cb1 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,9 @@
},
"ci": {
"version": "8",
+ "license": {
+ "year": 2017
+ },
"afterScript": false
},
"license": "MIT",
diff --git a/sequelize/README.md b/sequelize/README.md
index b89f628..cfd8355 100644
--- a/sequelize/README.md
+++ b/sequelize/README.md
@@ -8,7 +8,7 @@ The [egg] example project that uses [egg-sequelize] plugin. It will show you how
- install mysql and create database
-```
+```bash
brew install mysql # macOS
brew service start mysql
@@ -19,7 +19,7 @@ mysql
- install dependencies
-```
+```bash
npm install
```
@@ -27,7 +27,7 @@ npm install
- prepare database structure
-```
+```bash
# for develop
npm run sequelize -- db:migrate
# for unittest
@@ -36,13 +36,13 @@ NODE_ENV=test npm run sequelize -- db:migrate
- start project
-```
+```bash
npm run dev
```
- run test
-```
+```bash
npm test
```
diff --git a/sequelize/README.zh-CN.md b/sequelize/README.zh-CN.md
index c229895..fa78397 100644
--- a/sequelize/README.zh-CN.md
+++ b/sequelize/README.zh-CN.md
@@ -8,7 +8,7 @@
- 安装 mysql 并建立数据库
-```
+```bash
brew install mysql # macOS
brew service start mysql
@@ -19,7 +19,7 @@ mysql
- 安装 node 依赖
-```
+```bash
npm install
```
@@ -27,7 +27,7 @@ npm install
- 执行 migration 执行数据变更
-```
+```bash
# for develop
npm run sequelize -- db:migrate
# for unittest
@@ -36,13 +36,13 @@ NODE_ENV=test npm run sequelize -- db:migrate
- 启动项目
-```
+```bash
npm run dev
```
- 运行测试
-```
+```bash
npm test
```
diff --git a/sequelize/package.json b/sequelize/package.json
index 05da784..5f4cea6 100644
--- a/sequelize/package.json
+++ b/sequelize/package.json
@@ -21,7 +21,7 @@
},
"scripts": {
"dev": "egg-bin dev",
- "test": "egg-bin test",
+ "test": "NODE_ENV=test npm run sequelize -- db:migrate && egg-bin test",
"cov": "egg-bin cov",
"ci": "npm run cov",
"autod": "autod",