JavaWeb——使用Vue+Spring Boot實現(xiàn)Excel上傳

寫在最前

在上期教程中我們介紹了讀寫Excel與使用Selenium的入門方法,本期將介紹通過Vue+Spring Boot實現(xiàn)在WebApp中上傳Excel導(dǎo)入測試腳本的功能。使用前后端分離的技術(shù)是因為這樣便于后續(xù)功能的迭代,在本文中我們只涉及一個簡單的前端界面及一個簡單的后臺服務(wù)。

運行結(jié)果展示與源碼地址在文末,上期傳送門:Java自動化——使用Selenium+POI實現(xiàn)Excel自動化批量查單詞

步驟一覽

  1. 使用Vue-Cli創(chuàng)建前端項目
  2. Navbar編寫
  3. 腳本表格編寫
  4. 使用Spring Initializr創(chuàng)建后端項目
  5. pojo類的編寫
  6. UploadController的編寫
  7. UploadService的編寫
  8. 搭建簡單的RESTful API
  9. 運行服務(wù),編寫腳本并上傳

現(xiàn)在開始

  1. 使用Vue-Cli創(chuàng)建前端項目

    運用vue-cli工具可以很輕松地構(gòu)建前端項目,當(dāng)然,使用WebStorm來構(gòu)建會更加簡潔(如圖)。本文推薦使用WebStorm,因為在后續(xù)開發(fā)中,IDE會使我們的開發(fā)更加簡潔。部分配置如圖:

image
image
image
image
  1. Navbar編寫

    作為一個WebApp,Navbar作為應(yīng)用的導(dǎo)航欄是必不可少的。在本項目中,筆者引入了bootstrap對Navbar進行了輕松地構(gòu)建。在vue中我們需要在components文件夾中將我們的組件加進去,對于本工程來說,Navbar是我們要加入的第一個組件,他獨立于router之外,一直固定在網(wǎng)頁上方。

    2.1 首先,我們使用npm來安裝vue,vue-cli,bootstrap

    npm install vue
    npm install -g vue-cli
    npm install --save bootstrap jquery popper.js
    

    2.2 接下來我們在components目錄下new一個vue組件,并且在main.js中引入bootstrap依賴:

    import 'bootstrap/dist/css/bootstrap.min.css'
    import 'bootstrap/dist/js/bootstrap.min'
    

    2.3 下面就可以開始寫代碼了,由于本文只關(guān)注table相關(guān)的功能,所以導(dǎo)航欄中除了Script意外的元素都已經(jīng)disable,代碼如下:

<template>
  <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <span class="navbar-brand mb-0 h1">Vue-SpringBoot</span>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item">
          <router-link class="nav-link" to="/home">Home</router-link>
        </li>
        <li class="nav-item active">
          <router-link to="/" class="nav-link">Script</router-link>
        </li>
        <li class="nav-item">
          <router-link to="/history" class="nav-link">History</router-link>
        </li>
      </ul>
    </div>
  </nav>
</template>

<script>
    export default {
        name: "MyNavbar"
    }
</script>

<style scoped>

</style>

2.3 在App.vue中引入MyNavbar

  1. Script Table編寫

    作為自動化工具,必不可少的一部分就是引入Script,我們希望用戶能夠自由地使用H5界面進行Script的編寫,因此在這里使用了vue的數(shù)據(jù)雙向綁定進行Table CRUD。

    3.1 新建一個vue組件ScriptTable,代碼如下:

<template>
  <div class="container-fluid" id="scriptTable">
    <h3>My Script</h3>
    <form style="margin-top: 1rem">
      <input type="file" @change="getFile($event)" class="" multiple/>
      <input type="button" value="upload" @click="submit($event)" class="btn btn-dark">
    </form>
    <table class="table table-hover text-center table-bordered"
           style="word-break: break-all; word-wrap: break-word;margin-top: 1rem;">
      <thead>
      <th>#</th>
      <th>Platform</th>
      <th>Action</th>
      <th>Path</th>
      <th>Value</th>
      <th>Wait</th>
      <th>Screenshot</th>
      <th>Change</th>
      </thead>
      <tbody>
      <tr v-cloak v-for="(item, index) in steps">
        <th>{{index+1}}</th>
        <td>{{item.platform}}</td>
        <td>{{item.action}}</td>
        <td>{{item.path}}</td>
        <td>{{item.value}}</td>
        <td>{{item.wait}}</td>
        <td>{{item.screenshot}}</td>
        <td><a href="#" v-on:click="edit(item)">Edit</a> | <a href="#" v-on:click='aaa(index)'>Delete</a>
        </td>
      </tr>
      <tr>
        <th></th>
        <td><select class="form-control" v-model="stepstemp.platform">
          <option>Web</option>
          <option>Android</option>
        </select></td>
        <td><select class="form-control" v-model="stepstemp.action">
          <option>click</option>
          <option>get</option>
          <option>input</option>
          <option>swipe</option>
        </select></td>
        <td><input class="form-control" v-model="stepstemp.path" placeholder="Enter the xPath"></td>
        <td><input class="form-control" v-model="stepstemp.value" placeholder="Enter the input value"></td>
        <td><input class="form-control" v-model="stepstemp.wait" placeholder="Waiting seconds"></td>
        <td><select class="form-control" v-model="stepstemp.screenshot">
          <option>yes</option>
          <option>no</option>
        </select></td>
        <td>
          <button class="btn btn-sm btn-dark" v-on:click='save' v-if="isNotEdit">Save</button>
          <button class="btn btn-sm btn-primary" v-on:click='saveEdit' v-else>SaveEdit</button>
        </td>
      </tr>
      </tbody>
    </table>
    <hr/>
  </div>
</template>

<script>
  import Vue from 'vue'
  import axios from 'axios'

  export default {
    name: "ScriptTable",
    data() {
      return ({
        steps: [],
        stepstemp: {
          platform: '',
          action: '',
          path: '',
          value: '',
          wait: '',
          screenshot: ''
        },
        isNotEdit: true
      });
    },
    methods: {
      save: function () {
        this.steps.push(this.stepstemp);
        this.stepstemp = {
          platform: '',
          action: '',
          path: '',
          value: '',
          wait: '',
          screenshot: ''
        };
      },
      aaa: function (index) {
        this.steps.splice(index, 1)
      },
      edit: function (item) {
        this.isNotEdit = false;
        this.stepstemp = item;
      },
      saveEdit: function () {
        this.isNotEdit = true;
        this.stepstemp = {
          platform: '',
          action: '',
          path: '',
          value: '',
          wait: '',
          screenshot: ''
        };
      }
    }
  }
</script>


<style scoped>

</style>

3.3 運行dev,打開localhost:8080

npm run dev

前端頁面效果如下:

image

至此,本文相關(guān)的純前端部分完成地差不多了,加上mock的數(shù)據(jù)后,我們可以開始進行后端的開發(fā)了。

  1. 使用Spring Initializr創(chuàng)建后端項目

    為了更輕松地構(gòu)建工程,構(gòu)建RESTful API以及更輕松地配置請求處理,筆者選擇了Spring Boot作為后端框架。

    4.1 首先我們使用IDEA集成的Spring Initializr來構(gòu)建項目,部分配置如圖:

image
image

4.2 接下來在pom.xml中引入poi依賴,點擊import change。如下所示:

 <dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-ooxml</artifactId>
     <version>4.0.0</version>
 </dependency>

4.3 接下來我們在application.properties中配置server.port=8088,與前端項目分開

  1. pojo類Step的編寫

    下面是對pojo類的編寫,本文所需的pojo只有Step一種,與前端的table相對應(yīng),代碼如下:

import lombok.Data;
@Data
public class Step {
    private String platform;
    private String action;
    private String path;
    private String value;
    private int wait;
    private String screenshot;
}
  1. UploadController的編寫

    接下來是對前端Post請求的Handler(Controller)進行編寫,我們將上傳這個Post請求與"/uploadfile"相對應(yīng),注意加入@CrossOrigin注解實現(xiàn)跨域,代碼如下:

package com.daniel.vuespringbootuploadbe;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

@Controller
@CrossOrigin
@ResponseBody
public class UploadController {

    private static String UPLOADED_FOLDER = "src/main/resources/static/temp/";

    @Autowired
    private LoadService loadService;

    @PostMapping("/upload")
    public List<Step> singleFileUpload(MultipartFile file) {
        try {
            // Get the file and save it somewhere
            byte[] bytes = file.getBytes();
            Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
            Files.write(path, bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // Print file data to html
        List<Step> result = loadService.castToStep(new File(UPLOADED_FOLDER + file.getOriginalFilename()));
        return result;
    }

}

  1. LoadService的編寫

    下面該編寫Service來讀取請求中傳送的文件了,簡單地來說只有一個步驟,將Excel中的Script轉(zhuǎn)換為pojo的鏈表并在Controller中作為ResponseBody返回.

    7.1 首先創(chuàng)建Service接口,代碼如下:

package com.daniel.vuespringbootuploadbe;

import org.springframework.stereotype.Service;

import java.io.File;
import java.util.List;

@Service
public interface LoadService {
    List<Step> castToStep(File file);
}

7.2 接下來創(chuàng)建Service實現(xiàn)類,代碼如下:

package com.daniel.vuespringbootuploadbe;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class LoadServiceImpl implements LoadService {


    @Override
    public List<Step> castToStep(File file) {
        List<Step> steps = new ArrayList<>();
        Workbook workbook = null;
        try {
            workbook = new XSSFWorkbook(file);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        }
        Sheet sheet = workbook.getSheetAt(0);
        int num = sheet.getLastRowNum() - sheet.getFirstRowNum();
        //Read steps
        for (int i = 0; i < num; i++) {
            Row row = sheet.getRow(i+1);
            Step step = new Step();
            step.setPlatform(row.getCell(0).getStringCellValue());
            step.setAction(row.getCell(1).getStringCellValue());
            step.setPath(row.getCell(2).getStringCellValue());
            step.setValue(row.getCell(3).getStringCellValue());
            step.setWait((int) row.getCell(4).getNumericCellValue());
            step.setScreenshot(row.getCell(5).getStringCellValue());
            steps.add(step);
        }
        try {
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return steps;
    }
}

  1. 搭建簡單的RESTful API

    文章臨近尾聲,現(xiàn)在前后端的獨立代碼基本開發(fā)完畢,是時候搭建RESTful了,本文中的API非常簡單,就是對上傳做出響應(yīng),并將返回的json寫入界面上的Table中,完成Script導(dǎo)入,npm安裝axios后,在ScriptTable組件中加入如下代碼:

      getFile: function (event) {
        this.file = event.target.files[0];
        console.log(this.file);
      },
      submit: function (event) {
        event.preventDefault();
        let formData = new FormData();
        formData.append("file", this.file);
        axios.post('http://localhost:8088/upload', formData)
          .then(function (response) {
            for (let i = 0; i < response.data.length; i++) {
              var tempData = {
                platform: response.data[i].platform,
                action: response.data[i].action,
                path: response.data[i].path,
                value: response.data[i].value,
                wait: response.data[i].wait,
                screenshot: response.data[i].screenshot
              };
              this.steps.push(tempData);
            }
          }.bind(this))
          .catch(function (error) {
            alert("Fail");
            console.log(error);
          });
      }
  1. 運行服務(wù),編寫Script并上傳

    接下來我們創(chuàng)建一個Excel,按如圖格式編寫簡單Script,運行前后端服務(wù),實現(xiàn)上傳:

image

運行后,Excel文件會上傳到后端工程的static的temp目錄中

結(jié)果展示

image

結(jié)語

本文只是實現(xiàn)了基礎(chǔ)的上傳腳本功能,要實現(xiàn)腳本運行,我們還要在BE項目中實現(xiàn)相關(guān)服務(wù)進行封裝,需要Selenium的幫助。之后的教程中會做詳細闡述,敬請期待。

附錄

源碼地址:

前端項目——https://gitee.com/daniel_ddd/vue-springboot-upload-fe

后端項目——https://gitee.com/daniel_ddd/vue-springboot-upload-be

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容