美文网首页GIS加油站
node实现静态资源的上传发布

node实现静态资源的上传发布

作者: 牛老师讲webgis | 来源:发表于2022-05-01 10:56 被阅读0次

    概述

    需求是这样的:产品通过axure生成导出的html页面发给开发的时候需要安装插件,很不方便,为方便大家的协同,决定开发一个简单的协同共享工具,实现导出html页面的打包上传,并发布生成可访问的url。本文将讲述在node环境下如何实现上述的需求。

    实现

    image.png

    实现思路

    1. 通过Express实现上传接口;
    2. 通过shelljs调用服务器解压命令解压;
    3. 通过live-server实现解压资源的发布;
    4. 通过pm2实现node服务的运行与监控;

    实现代码

    1. 文件上传接口

    接口实现文件的上传,并解压到制定目录,返回可访问的url。

    router/file.js
    const express = require('express')
    const fs = require('fs')
    const path = require('path');
    const os = require('os')
    const shell = require('shelljs');
    const router = express.Router()
    const fileUtils = require('../utils/files')
    const config = require('../config')
    
    router.post('/upload', function (req, res) {
      const { code, name, version } = req.body
      const filePath = path.resolve(__dirname, '../')
    
      const systemUrl = `//${config.url}/demploy/${code}/${version}/www/`
    
      const jsonPath = `${filePath}/${config.root}/data/system.json`
      const json = fileUtils.readJson(jsonPath)
      if(!json[code]) json[code] = {}
      json[code]['name'] = name
      if(!json[code]['versions']) json[code]['versions'] = {}
      json[code]['versions'][version] = systemUrl
    // 将操作记录到JSON
      fileUtils.writeJson(jsonPath, json)
    
      const basePath = `${filePath}/${config.root}/demploy/${code}/${version}/`
      fileUtils.dirExists(basePath).then(() => {
        const des_file = basePath + req.files[0].originalname;
        fs.readFile(req.files[0].path, function (err, data) {
          fs.writeFile(des_file, data, function (err) {
            const platform = os.platform()
            let cmd = ''
            if(platform === 'win32') {
              cmd = `7z x ${des_file} -o${basePath}`
            } else if(platform === 'linux') {
              cmd = `unzip -O CP936 ${des_file} -d ${basePath}`
            }
            if(cmd) {
              shell.exec(cmd, function () {
                const response = {
                  code: 200,
                  url: systemUrl
                };
                res.end(JSON.stringify(response));
              })
            } else {
              res.end(JSON.stringify({code: 500}));
            }
          });
        });
      })
    })
    
    module.exports = router;
    
    app.js
    const express = require('express');
    const bodyParser = require('body-parser'); // 这个模块是获取post请求传过来的数据。
    const multer  = require('multer'); //multer - node.js 中间件,用于处理 enctype="multipart/form-data"(设置表单的MIME编码)的表单数据。
    const fileRouter= require('./router/file');
    const config = require('./config')
    
    const app = express();
    
    // 自定义跨域中间件
    const allowCors = function (req, res, next) {
      res.header('Access-Control-Allow-Origin', req.headers.origin);
      res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
      res.header('Access-Control-Allow-Headers', 'Content-Type');
      res.header('Access-Control-Allow-Credentials', 'true');
      next();
    };
    app.use(allowCors);//使用跨域中间件
    app.use(bodyParser.urlencoded({ extended: false })); // 判断请求体是不是json,不是的话把请求体转化为对象
    app.use(multer({ dest: path.resolve(__dirname, '/www/demploy/')}).array('file'));
    
    //文件服务上传服务
    app.use('/file', fileRouter);
    
    app.listen(18081, () => {
      console.log("接口已启动,访问地址为:http://localhost:18081")
    })
    

    2. live-server实现静态资源发布

    app.js
    liveServer.start({
      port: config.port, // Set the server port. Defaults to 8080.
      host: config.ip, // Set the address to bind to. Defaults to 0.0.0.0 or process.env.IP.
      root: `./${config.root}`, // Set root directory that's being served. Defaults to cwd.
      open: false, // When false, it won't load your browser by default.
      file: "index.html", // When set, serve this file (server root relative) for every 404 (useful for single-page applications)
    });
    
    config.js
    const config = {
      ip: '0.0.0.0',
      port: '8181',
      url: 'localhost:8181',
      root: 'www'
    }
    
    module.exports = config ;
    

    3. 前端页面实现

    index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>原型发布系统</title>
      <link rel="stylesheet" href="index.css">
    </head>
    <body>
    <div class="file-upload-panel">
      <h3>文件上传</h3>
      <div class="upload-box">
        <label>系统名称:</label>
        <input type="text" id="name" value="">
        <br><br>
        <label>系统编码:</label>
        <input type="text" id="code" value="">
        <br><br>
        <label>系统版本:</label>
        <input type="text" id="version" value="V1.0.0">
        <br><br>
        <label>部署文件:</label>
        <input type="file" id="file" accept="application/zip">
      </div>
      <ul class="tool-memo">
        <li><b style="color: red;">说明:</b></li>
        <li>1. 只能上传<span style="color: red;">*.zip</span>格式压缩文件,文件命名自定义; </li>
        <li>2. 静态页面资源需统一放在<span style="color: red;">www</span>目录下面; </li>
        <li>3. 参数均为必填参数,文件选择完成后会自动上传; </li>
        <div style="margin: 1rem 0;"><b>压缩包示例如下图:</b></div>
        <img src="demo.png">
      </ul>
    </div>
    <ul class="system-list" id="systemList"></ul>
    <script src="index.js"></script>
    </body>
    </html>
    
    index.css
    html, body {
        font-size: 16px;
    }
    
    .file-upload-panel {
        position: absolute;
        top: 2rem;
        right: 2rem;
        box-shadow: 1px 1px 5px #ccc;
        border-radius: 5px;
        font-size: 0.8rem;
    }
    .file-upload-panel .upload-box {
        padding: 1.5rem;
    }
    .file-upload-panel h3 {
        padding: 0.8rem 1.2rem;
        margin: 0;
        border: 1px solid #ccc;
    }
    .system-list h5 {
        margin: 0.8rem 0 0.8rem 0;
        padding: 0;
    }
    .url-list {
        margin: 0;
        padding: 0 1rem;
    }
    .url-list li {
        height: 1.8rem;
        line-height: 1.8rem;
    }
    .url-list span {
        margin-right: 0.8rem;
    }
    
    .tool-memo {
        margin: 0;
        padding: 0 1.5rem 1.5rem 1.5rem;
        list-style: none;
    }
    .tool-memo li {
        line-height: 2;
    }
    .tool-memo li:not(:first-child) {
        padding-left: 1rem;
    }
    
    .upload-box input {
        width: calc(100% - 5rem);
    }
    
    index.js
    function refreshSystemList() {
      const url = `data/system.json`
      fetch(url).then(res => res.json()).then(res => {
        const systemList = document.getElementById('systemList')
        for(let sys in res) {
          const sysData = res[sys]
          const li = document.createElement('li')
          const h5 = document.createElement('h5')
          h5.innerText = sysData.name
          const ul = document.createElement('ul')
          ul.classList.add('url-list')
          const versions = sysData.versions
          for(let ver in versions) {
            const _li = document.createElement('li')
            _li.innerHTML = `<span>${ver}</span><a href="${versions[ver]}" target="_blank">${versions[ver]}</a>`
            ul.appendChild(_li)
          }
          li.appendChild(h5)
          li.appendChild(ul)
          systemList.appendChild(li)
        }
      })
    }
    
    refreshSystemList()
    
    const fileDom = document.getElementById('file')
    fileDom.onchange = function (e) {
      e.stopPropagation()
      const formData = new FormData();
      const file = fileDom.files[0]
      const code = document.getElementById('code').value
      const name = document.getElementById('name').value
      const version = document.getElementById('version').value
      if(!code || !name || !version) {
        alert('有参数未填写,请填写后再上传!')
        fileDom.value = ''
        return false
      }
      formData.append('file', file);
      formData.append('code', code)
      formData.append('name', name)
      formData.append('version', version)
      fetch(`//${window.location.hostname}:18081/file/upload`, {
        method: 'post',
        body: formData,
      }).then(response => response.json())
        .then((data) => {
          copy_to_clipboard(data.url)
          alert('系统发布成功,访问地址已复制,请在浏览器粘贴地址并访问!')
          window.location.reload()
        });
    }
    
    function copy_to_clipboard(txt_str){
      const input = document.createElement('input');
      document.body.appendChild(input);
      input.setAttribute('value', txt_str);
      input.select();
      if (document.execCommand('copy')) {
        document.execCommand('copy');
      }
      document.body.removeChild(input);
    }
    

    4. 部署

    通过pm2来进行系统的部署和监控,因此需要先执行命令npm i pm2 --global安装。安装完之后就可通过命令pm2 start ./app.js启动,启动后界面如下:

    image.png
    pm2其他常用命令如下:
    # 停止服务
    pm2 stop app # app_name | app_id
    
    # 查看进程
    pm2 list
    
    # 查看进程信息
    pm2 describe 0 # app_id
    
    # 查看日志
    pm2 logs app [--lines 1000]
    

    相关文章

      网友评论

        本文标题:node实现静态资源的上传发布

        本文链接:https://www.haomeiwen.com/subject/naauyrtx.html