解決java POI導入Excel超時問題


由於要導入大量數據,后台會耗費很長時間,導致超時。

本項目前端request.js中設定的超時時間為150s.

const service = axios.create({
    baseURL: baseUrl,
    withCredentials: true,
    timeout: 150000
});

我們的做法是:

前端導入Excel向后台發出請求時,后台立即返回信息“后台正在導入,請稍等!”,向redis中存入鍵isFinished的值為字符串“0”,並且開啟一個線程來完成插入大量數據到數據庫的工作,當插入完成則將redis中isFinished的值改為字符串“1”。前端收到“后台正在輸入,請稍等!”后,此時導入按鈕處於禁用狀態並且處於加載狀態。通過window.setInterval定時輪詢,每隔30秒去后台查詢ifFinished值,如果為字符串“1”表示上傳完畢,此時在彈框“上傳完畢!”,此時導入按鈕禁用狀態和加載狀態都取消,並且刷新列表。

前端代碼:

<div style="margin-bottom: 10px">
        <el-form :inline="true">
          <el-form-item label="">
            <el-button type="success" icon="el-icon-download" @click="downloadTemplate">下載模板</el-button>
            <el-button :loading="importLoading" type="primary" icon="el-icon-upload" @click="handleImport">導入
            </el-button>
            <el-upload ref="importUpload" :auto-upload="false" :show-file-list="false" :on-change="handleUploadChange"
              :disabled="importDisabled" style="display: inline" action="#" class="upload-demo">
              <el-button id="uploadButton" style="display: none" slot="trigger" :loading="importLoading" size="small"
                type="primary" icon="el-icon-upload">導入</el-button>
            </el-upload>
          </el-form-item>
          <el-form-item label="">
            <el-button type="warning" icon="el-icon-lightning" @click="exportExcel">導出</el-button>
          </el-form-item>
        </el-form>
      </div>
 
         
handleUploadChange(file) {
if (file.name.lastIndexOf('.') < 0) {
this.$message.error('上傳文件只能是xls、xlsx格式!')
return
}
const testMsg = file.name.substring(file.name.lastIndexOf('.') + 1).toLowerCase()
const extensionXLS = testMsg == 'xls'
const extensionXLSX = testMsg == 'xlsx'
if (!extensionXLS && !extensionXLSX) {
this.$message.error('上傳文件只能是xls、xlsx格式!')
return
}
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
this.$message.error('上傳文件不能超過 2MB!')
return
}
this.importLoading = true
this.importDisabled = true
const data = new FormData()
data.append('file', file.raw)
medicineListApi.importExcel(data).then(response => {
if (response.status == true) {
this.open2(response.msg)
this.getList()
} else {
this.open2(response.msg)
this.importLoading = false
this.importDisabled = false
}
window.setInterval(() => {
setTimeout(this.getStatus(), 0)
}, 30*1000)
}).catch(() => {
this.open2('抱歉,導入失敗')
this.importLoading = false
this.importDisabled = false
})

},
open2(str) {
this.$notify({
title: '提示',
message: str,
duration: 0
})
},
// 請求后台獲取是否導入完成的狀態
getStatus(){
medicineListApi.getStatus().then(response => {
if (response.data == "1") {
this.open2("上傳完畢!")
this.importLoading = false
this.importDisabled = false
this.getList()
}
})
}

項目中我們經常需要實現輪詢-每隔幾秒請求一次接口刷新數據,一般都會使用setInterval,但要注意單純使用它會導致頁面卡死,解釋:setInterval不會清除定時器隊列,每重復執行1次都會導致定時器疊加,最終卡死你的網頁。但是setTimeout是自帶清除定時器的

解決辦法:

window.setInterval(() => {
  setTimeout(fun, 0)
}, 30000)

setTimeout() 方法用於在指定的毫秒數后調用函數或計算表達式。 如果你只想重復執行可以使用 setInterval() 方法。setTimeout()只執行一次,而setInterval可以多次調用。

medicineList.js代碼:

import request from "./request";

const baseUrl = "/medicineList"
export const medicineListApi = {
  /**
   * 導入
   * @param data
   */
  importExcel(data) {
    return request({
      url: baseUrl + '/import',
      method: 'post',
      data: data,
      headers: {
        'Content-Type': 'multipart/form-data'
      }
    })
  },
  getStatus() {
    return request({
      url: baseUrl + "/getStatus",
      method: "GET"
    });
  },
};

后台代碼:

controller:

@RestController
@RequestMapping("/medicineList")
@Slf4j
public class MedicineListController extends BaseController {

    @Autowired
    private MedicineListService medicineListService;

    /**
     * 導入葯品信息
     *
     * @param file
     * @return
     * @throws Exception
     */
    @PostMapping("/import")
    public JSONObject importNodeInfo(MultipartFile file) throws Exception {
        return medicineListService.importNodeInfo(file.getInputStream());
    }

    @GetMapping("/getStatus")
    public JSONObject getStatus() {
        return medicineListService.getStatus();
    }
}

service接口:

public interface MedicineListService extends IService<DrugData> {
    JSONObject importNodeInfo(InputStream inputStream) throws Exception;

    JSONObject getStatus();
}

service實現類:

@Service
public class MedicineListServiceImpl extends ServiceImpl<MedicineListMapper,DrugData> implements MedicineListService {

    @Resource
    private MedicineListMapper medicineListMapper;

    private static Logger logger = LoggerFactory.getLogger(MedicineListService.class);
    @Autowired private StringRedisTemplate redisTemplate;

    public JSONObject importNodeInfo(InputStream in) throws Exception {
        redisTemplate.opsForValue().set("isFinished","0",60*60l,TimeUnit.SECONDS);
        JSONObject json = new JSONObject();
        json.put("msg", "后台正在導入,請稍等!");
        json.put("status", true);
        new Thread() {
            @Override
            public void run() {
                try {
                    //   根據類型進行分流導入
                    String str0 = "";
                    String str = "";
                    String fstr = "";
                    int operCount = 0;
                    XSSFWorkbook workbook = new XSSFWorkbook(in);
                    XSSFSheet sheet = workbook.getSheetAt(0);
                    int totalColumnNum = sheet.getRow(0).getLastCellNum();
                    logger.info("導入代碼信息excel文件的總列數:" + totalColumnNum);
                    System.out.println(totalColumnNum);
                    int lastRowNum = sheet.getLastRowNum();

                    logger.info("導入節點信息excel文件的總行數:" + lastRowNum);
                    System.out.println(sheet.getLastRowNum());
                    for (int num = 0; num <= lastRowNum; num++) {
                        XSSFRow row = sheet.getRow(num);
                        if(row == null) {
                            str0 = "存在空數據行,行號:" + (num + 1) + ",導入失敗!";
                            break;
                        }
                        int hcount = num + 1;
                        if (num == 0) {
                            if (null != String.valueOf(row.getCell(0)) && String.valueOf(row.getCell(0)).equals("葯品編碼")) {
                                continue;
                            } else {

                                json.put("msg", "導入的模板名稱出錯,請確認");
                                json.put("status", false);
                                json.put("data", operCount);

                            }
                        }
                        DrugData drugData = new DrugData();
                        String drugNo = String.valueOf(row.getCell(0));
                        if(StringUtils.isNotBlank(drugNo) && !"null".equalsIgnoreCase(drugNo)) {
                            drugData.setDrugno(drugNo);// 葯品編碼
                        }
                        String drugName = String.valueOf(row.getCell(1));
                        if(StringUtils.isNotBlank(drugName) && !"null".equalsIgnoreCase(drugName)) {
                            drugData.setDrugname(drugName);//葯品名稱
                        }
                        String indiction = String.valueOf(row.getCell(2));
                        if(StringUtils.isNotBlank(indiction) && !"null".equalsIgnoreCase(indiction)) {
                            drugData.setIndiction(indiction); //適應症
                        }

                        try {
                            QueryWrapper<DrugData> wrapper = new QueryWrapper<>();
                            if(StringUtils.isNotBlank(drugData.getDrugno())){
                                wrapper.eq("drugno",drugData.getDrugno());
                            }
                            List<DrugData> drugDataList = medicineListMapper.selectList(wrapper);

                            if (null != drugDataList && drugDataList.size() > 0) {
                                drugData.setId(drugDataList.get(0).getId());
                                medicineListMapper.updateById(drugData);
                            } else {
                                medicineListMapper.insert(drugData);
                            }
                        } catch (Exception e) {
                            logger.error(e.getMessage());
                            str = str + "第【" + hcount + "】行,";
                            continue;
                        }
                        operCount++;
                    }
                    if (StringUtils.isNotBlank(str)) {
                        str = "其中-->" + str + "導入失敗!";
                    }

                    if (StringUtils.isNotBlank(fstr)) {
                        fstr = "==投量-->" + fstr + "附表導入失敗!";
                    }
                    redisTemplate.opsForValue().set("isFinished","1");

                    json.put("msg", "操作成功" + str0 + str + fstr);
                    json.put("status", true);
                    json.put("data", operCount);

                } catch (Exception e) {
                    logger.error(e.getMessage());
                } finally {
                    try {
                        in.close();
                    } catch (IOException e) {
                        logger.error(e.getMessage());
                    }
                }
            }
        }.start();
        return json;
    }
    @Override
    public JSONObject getStatus() {
        String isFinished = redisTemplate.opsForValue().get("isFinished");
        JSONObject result = new JSONObject();
        if (StringUtils.isNotBlank(isFinished)) {
            result.put("msg", "獲取成功");
            result.put("status", true);
            result.put("data",isFinished);
        } else {
            result.put("msg", "獲取失敗");
            result.put("status", false);
        }
        redisTemplate.delete("isFinished");
        return result;
    }
}

 注意:導入Excel時,String.valueOf(row.getCell(0))獲取的值出了要進行非空判斷外,還要判斷不為字符串null,再執行插入,否則Excel中未填寫的單元格插入數據庫會變成字符串null。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM