egg商城--專案準備篇

遨翔在知識的海洋裡發表於2019-02-13

建立專案

執行命令

cnpm i egg-init -g
egg-init egg01 --type=simple
cd egg01
cnpm install
npm run dev
複製程式碼

終端效果

egg商城--專案準備篇

egg商城--專案準備篇

配置路由,模板,資料庫,中介軟體

路由

app\router.js

  router.get('/', controller.user.index);
  router.get('/add', controller.user.add);
  router.get('/edit', controller.user.edit);

  router.post('/doAdd', controller.user.doAdd);
複製程式碼

模板

安裝依賴egg-view-ejs

cnpm install egg-view-ejs --save
複製程式碼

配置模板

config\plugin.js

exports.ejs = {
  enable: true,
  package: 'egg-view-ejs',
};
複製程式碼

config\config.default.js

  config.view = {
    mapping: {
      '.html': 'ejs',
    },
  };
複製程式碼

配置資料庫

安裝依賴egg-mongoose

cnpm install egg-mongoose --save
複製程式碼

配置資料庫

config\plugin.js

exports.mongoose = {
  enable: true,
  package: 'egg-mongoose',
};
複製程式碼

config\config.default.js

  config.mongoose = {
    client: {
      url: 'mongodb://egg01:123456@127.0.0.1/egg01',
      options: {},
    },
  };
複製程式碼

資料庫對映

app\model\user.js

module.exports = app => {
  const mongoose = app.mongoose;
  const Schema = mongoose.Schema;
  const UserSchema = new Schema({
    username: {
      type: String,
    },
    password: {
      type: String,
    },
    avator: {
      type: String,
    },
  });
  return mongoose.model('User', UserSchema, 'user');
};

複製程式碼

中介軟體csrf安全驗證

app\middleware\auth.js

module.exports = (options, app) => {
  return async function auth(ctx, next) {
    ctx.state.csrf = ctx.csrf;
    await next();
  };
}
;
複製程式碼

config\config.default.js

  config.middleware = [ 'auth' ];
複製程式碼

單檔案上傳

缺陷

圖片必須放在表單的最後面,並且圖片可能會丟失

view

程式碼

app\view\user\add.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div>csrf:
        <%= csrf %>
    </div>
    <form action="/doAdd?_csrf=<%=csrf%>" method="post" enctype="multipart/form-data">
        <div>
            <label>
                        <span>賬號</span>
                        <input type="text" name="username">
                    </label>
        </div>
        <div>
            <label>
                        <span>密碼</span>
                        <input type="text" name="password">
                    </label>
        </div>
        <div>
            <label>
                        <span>頭像</span>
                        <input type="file" name="avator">
                    </label>
        </div>
        <button type="submit">確定</button>
    </form>
</body>

</html>
複製程式碼

效果

egg商城--專案準備篇

controller

app\controller\user.js

'use strict';

const Controller = require('egg').Controller;
const fs = require('fs');
const path = require('path');
const pump = require('mz-modules/pump');
class HomeController extends Controller {
  async add() {
    await this.ctx.render('user/add');
  }
  
  async doAdd() {
    const stream = await this.ctx.getFileStream(); // 獲取表單提交的資料
    if (!stream.filename) { // 注意如果沒有傳入圖片直接返回
      return;
    }

    // 圖片上傳到靜態資料夾
    let target = 'app/public/upload/' + path.basename(stream.filename); // "filename":"c:/images/291051166-5b20eca6448e8_articlex.png",  path.basename  => 291051166-5b20eca6448e8_articlex.png
    const writeStream = fs.createWriteStream(target);
    await pump(stream, writeStream); // stream.pipe(writeStream);   //可以用, 但是不建議,因為不能處理失敗的情況

    // 存入資料庫
    target = target.slice(3);
    const newUser = Object.assign(stream.fields, {
      avator: target,
    });
    await this.ctx.service.user.doAdd(newUser);
    // 跳轉
    this.ctx.redirect('/');
  }
}

module.exports = HomeController;

複製程式碼

service

app\service\user.js

'use strict';

const Service = require('egg').Service;

class UserService extends Service {
  async doAdd(user) {
    const newUser = new this.ctx.model.User(user);
    newUser.save();
  }
}

module.exports = UserService;

複製程式碼

上傳效果

表單

egg商城--專案準備篇

上傳後返回給前端

egg商城--專案準備篇

儲存到專案後臺靜態資料夾

egg商城--專案準備篇

多檔案上傳

view

把上傳按鈕悠為<input type="file" multiple="multiple" name="avator">,檔案上傳變為多選

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div>csrf:
        <%= csrf %>
    </div>
    <form action="/doAdd?_csrf=<%=csrf%>" method="post" enctype="multipart/form-data">
        <div>
            <label>
                        <span>賬號</span>
                        <input type="text" name="username">
                    </label>
        </div>
        <div>
            <label>
                        <span>密碼</span>
                        <input type="text" name="password">
                    </label>
        </div>
        <div>
            <label>
                        <span>頭像</span>
                        <input type="file" multiple="multiple" name="avator">
                    </label>
        </div>
        <button type="submit">確定</button>
    </form>
</body>

</html>
複製程式碼

controller

  async doAdd() {
    // { autoFields: true }:可以將除了檔案的其它欄位提取到 parts 的 filed 中
    // 多個圖片/檔案
    const parts = this.ctx.multipart({
      autoFields: true,
    });
    const files = [];
    let stream;
    while ((stream = await parts()) != null) {
      if (!stream.filename) { // 注意如果沒有傳入圖片直接返回
        return;
      }
      const fieldname = stream.fieldname; // file表單的名字  face
      const target = 'app/public/upload/' + path.basename(stream.filename);
      const writeStream = fs.createWriteStream(target);
      await pump(stream, writeStream); // 寫入並銷燬當前流   (egg  demo提供的)

      files.push({
        [fieldname]: target, // es6裡面的屬性名錶達式
      });
    }

    const avator = files[0].avator.slice(3);  //只選第一張圖片
    const newUser = Object.assign(parts.field, {
      avator,
    });
    await this.ctx.service.user.doAdd(newUser);
    // 跳轉
    this.ctx.redirect('/');
  }
複製程式碼

效果

egg商城--專案準備篇

egg商城--專案準備篇

相關文章