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

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

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

項(xiàng)目場(chǎng)景:

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

問(wèn)題描述:

導(dǎo)入速度慢

原因分析:

一條條傳入時(shí)間過(guò)慢

解決方案:

前后端優(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" >點(diǎn)擊上傳</el-button>
  </el-upload>
</div>

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

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 電話(huà):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文件: 需要解析哪些字段定義好就行了,后端也可以用實(shí)體類(lèi)接收 我是直接前端做的

//文件按照二進(jìn)制格式讀取
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日開(kāi)始,減去25569天轉(zhuǎn)換為Unix時(shí)間戳
        date = new Date((v - 25569) * 86400 * 1000);
    } else {
        // 否則,假設(shè)輸入是標(biāo)準(zhǔn)的日期字符串
        date = new Date(v);
    }

    // 使用 toLocaleString 方法格式化日期和時(shí)間
    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)險(xiǎn)預(yù)算值',
        type:'int'
    },
    COMCODE:{
        text:'機(jī)構(gòu)代碼',
        type:'string'
    },
    COMNAME:{
        text:'機(jī)構(gòu)名稱(chēng)',
        type:'string'
    },
    NEWCHNLTYPE:{
        text:'清分后渠道類(lèi)型',
        type:'string'
    },
    BUDGET:{
        text:'預(yù)算值',
        type:'int'
    },
    
}

3.提交部分:

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

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

  // 針對(duì)特定表的確認(rèn)提示
  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)造請(qǐng)求體,包含所有數(shù)據(jù)和表名
    const requestData = {
      tableNames: this.tableNames,
      data: this.tableData  // 整個(gè)數(shù)組一次性發(fā)送
    };

    // 發(fā)送POST請(qǐng)求
    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: "上傳失敗,請(qǐng)檢查數(shù)據(jù)格式或網(wǎng)絡(luò)連接!",
      type: 'error',
      showClose: true
    });
  } finally {
    // 無(wú)論成功失敗都關(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ù)實(shí)體類(lèi)

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ù)層:

后端重點(diǎn):需要定義多線(xiàn)程實(shí)體類(lèi)然后引入serviceimpl進(jìn)行調(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(); // 核心線(xiàn)程數(shù)=CPU核心數(shù)
        int maxPoolSize = corePoolSize * 2; // 最大線(xiàn)程數(shù)=CPU核心數(shù)*2
        long keepAliveTime = 60; // 空閑線(xiàn)程存活時(shí)間
        return new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000), // 任務(wù)隊(duì)列容量
                new ThreadPoolExecutor.CallerRunsPolicy() // 隊(duì)列滿(mǎn)時(shí),主線(xiàn)程兜底執(zhí)行
        );
    }
}

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

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. 初始化計(jì)數(shù)器(從DB讀取當(dāng)前值,避免重啟后計(jì)數(shù)重置)
    Integer dbCount = mapper.selectTaskData();
    totalCount.set(dbCount != null && dbCount > 0 ? dbCount : 0);

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

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

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

                // 5. 原子更新計(jì)數(shù)器(分片大小=當(dāng)前批次插入數(shù)量)
                int currentBatchSize = chunk.size();
                totalCount.addAndGet(currentBatchSize);
                Integer DATA = mapper.selectTaskData();
                if (DATA > 0) {
                    // 計(jì)數(shù)器加1后再更新到數(shù)據(jù)庫(kù)
                    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ù)完成,計(jì)數(shù)器減1
            }
        });
    }

    // 等待所有線(xiàn)程執(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實(shí)現(xiàn)excle導(dǎo)入優(yōu)化解決方案的文章就介紹到這了,更多相關(guān)vue2+js+springboot實(shí)現(xiàn)excle導(dǎo)入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue?this.$router和this.$route區(qū)別解析及路由傳參的2種方式?&&?this.$route的各種語(yǔ)法

    vue?this.$router和this.$route區(qū)別解析及路由傳參的2種方式?&&?this.$route

    this.$router?相當(dāng)于一個(gè)全局的路由器對(duì)象,包含了很多屬性和對(duì)象(比如?history?對(duì)象),任何頁(yè)面都可以調(diào)用其?push(),?replace(),?go()?等方法,本文給大家介紹Vue中this.$router與this.$route的區(qū)別?及push()方法,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • vue后臺(tái)系統(tǒng)管理項(xiàng)目之角色權(quán)限分配管理功能(示例詳解)

    vue后臺(tái)系統(tǒng)管理項(xiàng)目之角色權(quán)限分配管理功能(示例詳解)

    這篇文章主要介紹了vue后臺(tái)系統(tǒng)管理項(xiàng)目-角色權(quán)限分配管理功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • vue項(xiàng)目中全局引入1個(gè).scss文件的問(wèn)題解決

    vue項(xiàng)目中全局引入1個(gè).scss文件的問(wèn)題解決

    這篇文章主要跟大家介紹了vue項(xiàng)目中全局引入1個(gè).scss文件的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 在vue中多次調(diào)用同一個(gè)定義全局變量的實(shí)例

    在vue中多次調(diào)用同一個(gè)定義全局變量的實(shí)例

    今天小編就為大家分享一篇在vue中多次調(diào)用同一個(gè)定義全局變量的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • vue修改打包配置如何實(shí)現(xiàn)代碼打包后的自定義命名

    vue修改打包配置如何實(shí)現(xiàn)代碼打包后的自定義命名

    這篇文章主要介紹了vue修改打包配置如何實(shí)現(xiàn)代碼打包后的自定義命名,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Vue路由(router-link)之高亮、動(dòng)態(tài)傳參詳解

    Vue路由(router-link)之高亮、動(dòng)態(tài)傳參詳解

    文章介紹了如何使用Vue Router進(jìn)行聲明式導(dǎo)航,并詳細(xì)講解了如何通過(guò)`router-link`組件實(shí)現(xiàn)導(dǎo)航高亮、自定義高亮類(lèi)名、查詢(xún)參數(shù)傳參和動(dòng)態(tài)路由傳參,此外,還提到了動(dòng)態(tài)路由參數(shù)的可選符
    2025-11-11
  • vue實(shí)現(xiàn)點(diǎn)擊追加選中樣式效果

    vue實(shí)現(xiàn)點(diǎn)擊追加選中樣式效果

    今天小編就為大家分享一篇vue實(shí)現(xiàn)點(diǎn)擊追加選中樣式效果,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-11-11
  • Element-UI中關(guān)于table表格的那些騷操作(小結(jié))

    Element-UI中關(guān)于table表格的那些騷操作(小結(jié))

    這篇文章主要介紹了Element-UI中關(guān)于table表格的那些騷操作(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • vue 中自定義指令改變data中的值

    vue 中自定義指令改變data中的值

    這篇文章主要介紹了vue 中自定義指令改變data中的值,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-06-06
  • 安裝vue/cli后查看版本顯示找不到vue指令的解決

    安裝vue/cli后查看版本顯示找不到vue指令的解決

    這篇文章主要介紹了安裝vue/cli后查看版本顯示找不到vue指令的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10

最新評(píng)論

金寨县| 三门峡市| 青河县| 大名县| 清流县| 郎溪县| 阿尔山市| 义乌市| 兴化市| 武乡县| 新沂市| 西城区| 青海省| 遂昌县| 偏关县| 石棉县| 连平县| 淮南市| 乌什县| 临泽县| 拉孜县| 远安县| 二连浩特市| 鄂州市| 黑山县| 梅州市| 衡阳县| 屏东市| 岚皋县| 青阳县| 三穗县| 武川县| 阳信县| 裕民县| 凤城市| 陆川县| 元氏县| 临朐县| 任丘市| 兴国县| 重庆市|