最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端vue2+js+springboot實現(xiàn)excle導(dǎo)入優(yōu)化解決方案

 更新時間:2025年11月03日 10:07:05   作者:Luffy船長  
這篇文章主要介紹了前端vue2+js+springboot實現(xiàn)excle導(dǎo)入優(yōu)化的相關(guān)資料,前端優(yōu)化包括使用ElementUI組件和封裝的JS解析函數(shù),后端重點在于多線程處理,通過定義線程安全技術(shù)及數(shù)據(jù)分片實現(xiàn)高效導(dǎo)入,需要的朋友可以參考下

項目場景:

在一些涉及報表的功能時候會需要導(dǎo)入excle數(shù)據(jù),之前寫過一個是一條一條傳入的,數(shù)據(jù)傳輸太慢了,所以結(jié)合網(wǎng)絡(luò)資料整了一個優(yōu)化

問題描述:

導(dǎo)入速度慢

原因分析:

一條條傳入時間過慢

解決方案:

前后端優(yōu)化

前端:

1.采用elementui組件

<el-button type="warning" icon="el-icon-folder-add" style="margin-left: 180px;" @click="submit" :disabled="disable">提交文件</el-button>
<div style="margin-left: 260px;margin-top: -40px">
  <el-upload
      action
      :on-change="handle"
      :auto-upload="false"
      :show-file-list="false"
      accept=".xls, .xlsx"
  >
    <el-button type="primary" icon="el-icon-upload" >點擊上傳</el-button>
  </el-upload>
</div>

2.js部分調(diào)用自己封裝好的js對數(shù)據(jù)進行解析

async handle(ev) {
  //console.log(this.options.label)
  let file = ev.raw;
  if (!file) return;
  let loadingInstance = Loading.service({
    text: "拼命加載中",
    background: 'rgba(0,0,0,.5)'
  })
  await delay(1000);
  let data = await readFile(file);
  let workbook = this.XLSX.read(data, {type: "binary"}),
      worksheet = workbook.Sheets[workbook.SheetNames[0]]
  data = this.XLSX.utils.sheet_to_json(worksheet);
  /**
   * 把讀取的數(shù)據(jù)轉(zhuǎn)出傳遞給后端的數(shù)據(jù)(姓名:name 電話:phone)
   * @type {*[]}
   */
  let arr = [];
  data.forEach(item => {
    let obj = {};
    for (let key in character) {
      if (!character.hasOwnProperty(key)) break;
      let v = character[key],
          text = v.text,
          type = v.type;
      v = item[text] || "";
      // console.log(type)
      // console.log(v)
      type === "string" ? v = (String(v)) : null;
      type === "number" ? v = (Number(v) * 100).toFixed(2) : null;
      type === "int" ? v = Math.round(Number(v)) : null;
      type === "time" ? v = convertToStandardTime(v) : null;
      obj[key] = v;
    }
    arr.push(obj);
  })
  await delay(100)
  this.tableData = arr;
  loadingInstance.close();
  this.disable = false;
  this.$message({
    message: '上傳成功!!',
    type: 'success',
    showClose: true
  });
  //console.log(arr)

},

js文件: 需要解析哪些字段定義好就行了,后端也可以用實體類接收 我是直接前端做的

//文件按照二進制格式讀取
export function readFile(file){
    return new Promise(resolve => {
        let reader = new  FileReader();
        reader.readAsBinaryString(file);
        reader.onload = ev => {
            resolve(ev.target.result);
        }
    })
}
//設(shè)置異步延遲
export function delay(interval = 0){
    return new Promise(resolve => {
        let timer = setTimeout(_=>{
            clearTimeout(timer);
            resolve();
        },interval)
    })
}
export function convertToStandardTime(v) {
    let date;

    // 檢查輸入是否為 Excel 的日期格式(天數(shù))
    if (typeof v === 'number' && v > 25569) {
        // Excel 的日期從1900年1月1日開始,減去25569天轉(zhuǎn)換為Unix時間戳
        date = new Date((v - 25569) * 86400 * 1000);
    } else {
        // 否則,假設(shè)輸入是標(biāo)準(zhǔn)的日期字符串
        date = new Date(v);
    }

    // 使用 toLocaleString 方法格式化日期和時間
    const standardTime = date.toLocaleString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        // hour: '2-digit',
        // minute: '2-digit',
        // second: '2-digit',
        hour12: false
    });

    return standardTime;
}

export let character = {
    YEAR:{
        text:"年份",
        type:'string'
    },
    CYCLE:{
        text:'周期 季度1q、2q 月度01m、02m.. ',
        type:'string'
    },
    NXBUDGET:{
        text:'農(nóng)險預(yù)算值',
        type:'int'
    },
    COMCODE:{
        text:'機構(gòu)代碼',
        type:'string'
    },
    COMNAME:{
        text:'機構(gòu)名稱',
        type:'string'
    },
    NEWCHNLTYPE:{
        text:'清分后渠道類型',
        type:'string'
    },
    BUDGET:{
        text:'預(yù)算值',
        type:'int'
    },
    
}

3.提交部分:

async submit() {
  if (this.tableData.length <= 0) {
    this.$message({
      message: '請先選擇一個Excel文件!',
      type: 'warning',
      showClose: true
    });
    return;
  }

  // 檢查是否選擇了目標(biāo)表
  if (!this.tableNames) {
    this.$message({
      message: '請先選擇一個需要傳入的表',
      type: 'warning',
      showClose: true
    });
    return;
  }

  // 針對特定表的確認提示
  if (this.tableNames === 'CONNECTION') {
    try {
      await this.$confirm(
          '此操作只能導(dǎo)入兩年內(nèi)的數(shù)據(jù),大于兩年的數(shù)據(jù)不做生效是否繼續(xù)!',
          '提示',
          {
            confirmButtonText: '確定',
            cancelButtonText: '取消',
            type: 'warning',
            center: true
          }
      );
    } catch (e) {
      this.$message({
        type: 'info',
        message: '已取消導(dǎo)入!'
      });
      return;
    }
  }

  // 顯示加載狀態(tài)
  this.disable = true;
  const loadingInstance = Loading.service({
    text: "正在上傳數(shù)據(jù)",
    background: 'rgba(0,0,0,.5)'
  });

  try {
    // 構(gòu)造請求體,包含所有數(shù)據(jù)和表名
    const requestData = {
      tableNames: this.tableNames,
      data: this.tableData  // 整個數(shù)組一次性發(fā)送
    };

    // 發(fā)送POST請求
    const response = await this.$axios.post('xxxx/aaaaa', requestData);

    if (parseInt(response.code) === 200) {
      this.$message({
        message: '數(shù)據(jù)傳輸完畢!!!',
        type: 'success',
        showClose: true
      });
    } else {
      this.$message({
        message: response.msg || '上傳失敗',
        type: 'error',
        showClose: true
      });
    }
  } catch (error) {
    console.error('數(shù)據(jù)異常:', error);
    this.$message({
      message: "上傳失敗,請檢查數(shù)據(jù)格式或網(wǎng)絡(luò)連接!",
      type: 'error',
      showClose: true
    });
  } finally {
    // 無論成功失敗都關(guān)閉加載狀態(tài)
    this.disable = false;
    loadingInstance.close();
  }
},

后端部分:

1.控制層

@PostMapping("/xxxx")
public AjaxResult importAllCCSData(@RequestBody ExcelImportRequest request) {
    try {
        List<Map<String, Object>> excelData = request.getData();
        DataSourceUtil.setDB("db2");
        importDataService.setCCSDatas(excelData);
        // 這里可以添加批量處理數(shù)據(jù)的邏輯

        return AjaxResult.success("數(shù)據(jù)接收成功");
    } catch (Exception e) {
        e.printStackTrace();
        return AjaxResult.error("數(shù)據(jù)接收失敗: " + e.getMessage());
    }
}

2.vo層定義控制層接收的參數(shù)實體類

public class ExcelImportRequest {
    private String tableNames;
    private List<Map<String, Object>> data;  // 接收Excel中的所有數(shù)據(jù)

    // getter和setter方法
    public String getTableNames() {
        return tableNames;
    }

    public void setTableNames(String tableNames) {
        this.tableNames = tableNames;
    }

    public List<Map<String, Object>> getData() {
        return data;
    }

    public void setData(List<Map<String, Object>> data) {
        this.data = data;
    }
}

3.定義業(yè)務(wù)層:

后端重點:需要定義多線程實體類然后引入serviceimpl進行調(diào)用

ThreadPoolConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
public class ThreadPoolConfig {

    @Bean(name = "batchInsertPool")
    public ThreadPoolExecutor batchInsertPool() {
        int corePoolSize = Runtime.getRuntime().availableProcessors(); // 核心線程數(shù)=CPU核心數(shù)
        int maxPoolSize = corePoolSize * 2; // 最大線程數(shù)=CPU核心數(shù)*2
        long keepAliveTime = 60; // 空閑線程存活時間
        return new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000), // 任務(wù)隊列容量
                new ThreadPoolExecutor.CallerRunsPolicy() // 隊列滿時,主線程兜底執(zhí)行
        );
    }
}

引入batchInsertPool并注冊定義線程安全技術(shù)及和分片用于計算條數(shù)和根據(jù)劃分的片的大小進行分批插入

void setCCSDatas(List<Map<String, Object>> excelData) throws InterruptedException;
@Override
public void setCCSDatas(List<Map<String, Object>> excelData) throws InterruptedException {
    if (excelData == null || excelData.isEmpty()) {
        return;
    }

    // 1. 初始化計數(shù)器(從DB讀取當(dāng)前值,避免重啟后計數(shù)重置)
    Integer dbCount = mapper.selectTaskData();
    totalCount.set(dbCount != null && dbCount > 0 ? dbCount : 0);

    // 2. 數(shù)據(jù)分片(將大列表拆成多個小列表)
    List<List<Map<String, Object>>> dataChunks = splitDataIntoChunks(excelData, BATCH_SIZE);

    // 3. 用CountDownLatch等待所有線程執(zhí)行完成
    CountDownLatch countDownLatch = new CountDownLatch(dataChunks.size());

    // 4. 線程池提交批量插入任務(wù)
    for (List<Map<String, Object>> chunk : dataChunks) {
        batchInsertPool.execute(() -> {
            try {
                DataSourceUtil.setDB("db2");
                // 批量插入當(dāng)前分片數(shù)據(jù)
                mapper.insertHBSZHConnectionCost(chunk);

                // 5. 原子更新計數(shù)器(分片大小=當(dāng)前批次插入數(shù)量)
                int currentBatchSize = chunk.size();
                totalCount.addAndGet(currentBatchSize);
                Integer DATA = mapper.selectTaskData();
                if (DATA > 0) {
                    // 計數(shù)器加1后再更新到數(shù)據(jù)庫
                    int newCount = totalCount.incrementAndGet(); // 先自增,返回新值
                    System.err.println(totalCount.get());
                    mapper.updateTaskData(totalCount.get());

                } else {
                    mapper.insertTaskData(1);
                    totalCount.set(0);
                    mapper.updateTaskData(1);
                }
            } catch (Exception e) {
                e.printStackTrace();
                // 異常處理:可記錄失敗分片,后續(xù)重試
            } finally {
                countDownLatch.countDown(); // 任務(wù)完成,計數(shù)器減1
            }
        });
    }

    // 等待所有線程執(zhí)行完畢,再繼續(xù)后續(xù)邏輯
    countDownLatch.await();
    System.out.println("所有數(shù)據(jù)插入完成,總插入條數(shù):" + totalCount.get());
}

4.數(shù)據(jù)分片的代碼

// 數(shù)據(jù)分片工具方法:將大列表拆成指定大小的小列表
private List<List<Map<String, Object>>> splitDataIntoChunks(List<Map<String, Object>> data, int batchSize) {
    List<List<Map<String, Object>>> chunks = new ArrayList<>();
    for (int i = 0; i < data.size(); i += batchSize) {
        int end = Math.min(i + batchSize, data.size());
        chunks.add(data.subList(i, end));
    }
    return chunks;
}

5.數(shù)據(jù)層

void insertHBSZHConnectionCost(@Param("list") List<Map<String, Object>> dataList);
<insert id="insertHBSZHConnectionCost">
    <!--         insert into HBTable (policyNo,docHandFeeRate,NONDOCHANDFEERATE,overallHandFeeRate,NOTES,IMPORTUSER,IMPORTTIME,DATATYPE,ISSUM)
            values (#{data.POLICYNO},#{data.DOCHANDFEERATE},#{data.NONDOCHANDFEERATE},#{data.OVERALLHANDFEERATE},#{data.NOTES},#{data.IMPORTUSER},CURRENT_TIMESTAMP,#{data.DATATYPE},#{data.ISSUM})-->
    insert into HB_SZH_CONNECTION_COST
    (policyno,dochandfeerate,nondochandfeerate,overallhandfeerate,notes,importuser,importtime,datatype,issum)
    values
    <foreach collection="list" item="data" separator=",">
        (#{data.POLICYNO},#{data.DOCHANDFEERATE},#{data.NONDOCHANDFEERATE},
        #{data.OVERALLHANDFEERATE},#{data.NOTES},#{data.IMPORTUSER},
        CURRENT_TIMESTAMP,#{data.DATATYPE},#{data.ISSUM})
    </foreach>
</insert>

到這里基本功能就結(jié)束了

總結(jié)

到此這篇關(guān)于前端vue2+js+springboot實現(xiàn)excle導(dǎo)入優(yōu)化解決方案的文章就介紹到這了,更多相關(guān)vue2+js+springboot實現(xiàn)excle導(dǎo)入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

鞍山市| 周至县| 江孜县| 天峻县| 濉溪县| 谢通门县| 临猗县| 乌苏市| 筠连县| 沾化县| 洛南县| 昭平县| 昆明市| 德庆县| 兰溪市| 离岛区| 罗定市| 金坛市| 尚志市| 烟台市| 绿春县| 望谟县| 伊金霍洛旗| 辉县市| 荔波县| 邵阳市| 腾冲县| 高邑县| 天长市| 雷州市| 蒙山县| 岑巩县| 札达县| 安康市| 富宁县| 隆德县| 满城县| 清苑县| 子洲县| 温宿县| 平凉市|