6

使用springBoot实现服务端XML文件的前端读写

 3 years ago
source link: https://segmentfault.com/a/1190000040227953
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

使用springBoot实现服务端XML文件的前端读写

通常我们修改某个服务的配置文件的时候,需要登入服务器,进入指定目录然后对配置文件例如xml进行修改,偶尔一次操作还可以,但是频繁操作确实有点麻烦。
所以我们直接把这项功能放到前端界面来进行操作,来提升运维的效率。

  1. 后端通过I/O来将服务端XML文件读取并发送给前端
  2. 前端把接收回来的xml文件在前端web编辑器里进行渲染
  3. 前端把修改过的xml文件再次发送给后端
  4. 后端把接收回来的xml写回服务端配置文件

image.png

1.springBoot
2.dom4j(xml读写库)
3.vue-element-admin
4.ace.js(web编辑器)

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>

resources目录下新建hdfs-site.xml,并写入如下内容进行测试

测试demo中路径为写死的,后续可改为动态配置即可

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration> 
  <!-- 指定HDFS副本的数量 -->  
  <property> 
    <name>dfs.replication</name>  
    <value>1.2</value> 
  </property>  
  <!-- 指定Hadoop辅助名称节点主机配置 -->  
  <property> 
    <name>dfs.namenode.secondary.http-address</name>  
    <value>java151:50090</value> 
  </property>  
  <property> 
    <name>dfs.webhdfs.enabled</name>  
    <value>true</value> 
  </property> 
</configuration>

新建XmlUtil工具类

import org.dom4j.*;
import org.dom4j.io.*;
import java.io.*;

public class XmlUtil {
    /**
     * 获取xml文档
     */
    public static Document getDocument(String filename) {
        File xmlFile = new File(filename);
        Document document = null;
        if (xmlFile.exists()) {
            try {
                SAXReader saxReader = new SAXReader();
                document = saxReader.read(xmlFile);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return document;
    }

    /**
     * 写入xml节点,没有就新建,存在就覆盖
     */
    public static void coverElement(String filePath, Document document) throws Exception {
        if (document != null) {
            OutputFormat format = OutputFormat.createPrettyPrint();
            File xmlFile = new File(filePath);
            format.setEncoding("UTF-8");
            XMLWriter writer = new XMLWriter(new FileOutputStream(xmlFile), format);
            writer.write(document);
            writer.close();
        }
    }
}

新建getXmlFile接口,借助dom4jasXML()将xml以字符串形式返回给前端,Result类自行根据项目返回需求实现即可

@ApiOperation(value = "获取xml文件", notes = "获取xml文件")
@GetMapping(value = "/getXmlFile")
public Object getXmlFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String filePath = "src/main/resources/hdfs-site.xml";
    Document document = XmlUtil.getDocument(filePath);
    String docXmlText = document.asXML();//输出xml字符串
    return new Result(true, 20000, "获取hdfs-site.xml成功", docXmlText);
}

新建putXmlFile接口,借助DocumentHelper.parseText将前端返回的字符串转为xml

@ApiOperation(value = "修改xml文件", notes = "修改xml文件")
@ResponseBody()
@PostMapping(value = "/putXmlFile")
public Object putXmlFile(@RequestBody XmlDO body, HttpServletResponse response) throws Exception {
    Document document = DocumentHelper.parseText(body.getData());//字符串转xml
    String filePath = "src/main/resources/hdfs-site.xml";
    XmlUtil.coverElement(filePath, document);
    return new Result(true, 20000, "修改hdfs-site.xml成功", body.getData());
}

XmlDO

import lombok.Data;
@Data
public class XmlDO {
    private String type;
    private String data;
}
"xml-formatter": "^2.4.0"
"lodash": "^4.17.20",

api目录下新建xml.js接口,url根据自己的实际情况修改即可

import request from '@/utils/request'

export function getXmlFile() {
  return request({
    url: '/api/test/getXmlFile',
    method: 'get'
  })
}
export function putXmlFile(data) {
  return request({
    url: '/api/test/putXmlFile',
    method: 'post',
    data
  })
}

导入ace.js,包括主题以及语言
public/index.html写入

//css
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/ace/1.4.9/theme-xcode.css">
//js
<script src="https://cdn.bootcdn.net/ajax/libs/ace/1.4.9/ace.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/ace/1.4.9/ext-beautify.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/ace/1.4.9/ext-language_tools.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/ace/1.4.9/theme-xcode.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/ace/1.4.9/mode-xml.js"></script>

新建编辑器组件src/components/codeEditor/index.vue, lodash依赖自行isntall

<template>
  <div :id="Id" ref="editor" :style="{ 'max-height': height }" class="ace-editor" />
</template>

<script>
import uniqueId from 'lodash/uniqueId'
export default {
  name: 'AceEditor',
  props: {
    value: {
      type: String,
      default: ''
    },
    height: {
      type: String,
      default: '300px'
    },
    readOnly: {
      type: Boolean,
      default: false
    },
    mode: {
      type: String,
      default: 'json'
    }
  },
  data() {
    this.editor = null
    this.Id = uniqueId('aceEditor')
    return {
      annot: []
    }
  },
  computed: {
    code: {
      // 数据更新通知父组件更新数据
      set(val) {
        this.$emit('input', val)
      },
      get() {
        return this.value
      }
    }
  },
  watch: {
    code() {
      // 父组件中数据变化,同步到ace Editor
      // aceEditor.setValue调用后默认会全选所有文本内容,需要对光标进行特殊处理
      // 缓存光标位置
      const position = this.editor.getCursorPosition()
      this.syncData()
      this.editor.clearSelection()
      this.editor.moveCursorToPosition(position)
    },
    mode(mode) {
      this.changeMode(mode)
    },
    readOnly(b) {
      this.setReadOnly(b)
    }
  },
  mounted() {
    this.initEditor()
  },
  methods: {
    initEditor() {
      // eslint-disable-next-line no-undef
      this.editor = ace.edit(this.Id)
      this.editor.setTheme('ace/theme/xcode')
      // 编辑时同步数据
      this.editor.session.on('change', () => {
        this.code = this.editor.getValue()
      })
      this.editor.getSession().on('changeAnnotation', () => {
        this.annot = this.editor.getSession().getAnnotations()
        this.$emit('annot', this.annot)
        for (var key in this.annot) {
          // eslint-disable-next-line no-prototype-builtins
          if (this.annot.hasOwnProperty(key)) { console.log(this.annot[key].text + 'on line ' + ' ' + this.annot[key].row) }
        }
      })
      // 字体大小
      this.editor.setFontSize(14)
      this.syncData()
      this.syncOptions()
    },

    changeMode(modeName) {
      const mode = {
        yaml: 'ace/mode/yaml',
        json: 'ace/mode/json',
        xml: 'ace/mode/xml',
        javascript: 'ace/mode/javascript'
      }
      this.editor.session.setMode(mode[modeName])
    },
    setReadOnly(readOnly) {
      this.editor.setReadOnly(readOnly)
    },
    syncOptions() {
      this.setReadOnly(this.readOnly)
      this.changeMode(this.mode)
    },
    syncData() {
      this.editor.setValue(this.code)
    }
  }
}
</script>

<style scoped>
.ace-editor {
  position: relative;
  height: 800px;
  border: 1px solid #ccc;
  border-radius: 2px;
}
.ace-editor /deep/ .ace_print-margin {
  display: none;
}
</style>

新建页面src/views/xmlTest/index.vue

<template>
  <div id="contain">
    <div>
      <div id="select">
        <h3>修改配置文件(xml)</h3>
        <el-button type="primary" @click="testApi">修改</el-button>
      </div>
      <aceEditor
        v-model="resquestCode"
        height="800px"
        mode="xml"
        @annot="annot"
      />
    </div>
  </div>
</template>

<script>
import { getXmlFile, putXmlFile } from '@/api/xml'
import aceEditor from '@/components/codeEditor'
export default {
  name: '',
  components: { aceEditor },
  props: {},
  data() {
    return {
      errcode: [],
      resquestCode: ``
    }
  },
  watch: {

  },
  mounted() {
    this.getXml()
  },
  methods: {
    isError() {
      if (this.errcode.length > 0) {
        this.$message.error(`代码第${this.errcode[0].row + 1}行语法错误,${this.errcode[0].text}`)
        return true
      }
      return false
    },
    testApi() {
      if (!this.isError()) {
        this.format()
        this.select()
      }
    },
    getXml() {
      getXmlFile().then(res => {
        console.log('res: ', res)
        this.resquestCode = res.data || ''
        this.format()
      })
    },
    annot(e) {
      this.errcode = e
    },
    format() {
      var format = require('xml-formatter')
      this.resquestCode = format(this.resquestCode, {
        collapseContent: true
      })
    },
    select() {
      putXmlFile({ data: this.resquestCode }).then(res => {
        console.log('res: ', res)
        this.$message.success('修改成功')
      })
    }
  }
}
</script>

<style lang="scss">
#contain {
  margin: 0 20px;
  display: block !important;
  #select {
    .el-select .el-input {
      width: 100px;
    }
    .input-with-select .el-input-group__prepend {
      background-color: #fff;
    }
  }
}
#editor {
  width: 100%;
  height: 300px;
}
</style>

添加路由配置

  {
    path: '/xml',
    component: Layout,
    children: [
      {
        path: '',
        name: 'xml',
        component: () => import('@/views/xmlTest/index'),
        meta: { title: 'xml', icon: 'xml' }
      }
    ]
  }

image.png

image.png

更多问题欢迎加入前端交流群交流749539640


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK