CompletionService的使用分页查数据,写入excel

背景:商户中心对账单文件下载。

产品需求:商户对结算明细的数据2.5w分一个文件。个人2.5w数据在一个excel文件中,不会导致打开文件慢,但是数据在写入excel只能多线程。对于文件的导出主要是防止数据在内存中大量存在 不能及时的写到磁盘,故去和产品沟通一把。建议将数据分2k条写入一个excel文件,数据获取从mysql也是这个数据量级去分页查询。理论上会合理些,沟通后意见并未采纳(一般都挺难采纳,一句我理解你们技术的实现上可能更好但我不想商户整合2k的数据到2.5w CV大法。该场景就是伪命题,都对接我们支付平台了  还能手动对账不成???????  产品妹子还是很强势呐!!!!!)。那么只能实现产品需求。。。。。。

实现方案:将各个业务类型的数据通过定时获取写入到数据库(暂时还缺少数据平台),通过CountDownLatch判断各个业务类型的数据新增到统一表merchant_settle_detail。执行过后才操作excel文件导出的任务,2.5w一个写线程  写的数据通过分页查询获得

线程池分组:

package com.XX.merchant.service.task;

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

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

import com.XX.merchant.util.constants.ThreadConstants;

/**
 * <p>Title: ThreadTool.java</p>
 * <p>Description: 商户部件线程池管理</p>
 * <p>Copyright: Copyright (c) 2017 XXXX技术有限公司</p>
 * <p>Company: XXXX技术有限公司</p>
 * <p>Author:xxxx</p>
 * <p>CreateTime:2019年11月27日上午11:34:31
 */
@Configuration
public class ThreadToolsManager {

	// 商户线程池  不可和query的线程复用 商户线程(父)释放比query线程(子)晚 
	@Bean(value = "settleMerchantNoThreadPool")
	public ExecutorService buildConsumerQueueThreadPool( ) {
		int cpuCount = Runtime.getRuntime().availableProcessors();
		ExecutorService settleMerchantNoThreadPool = new ThreadPoolExecutor(cpuCount +1, cpuCount +1, 60L, TimeUnit.SECONDS,
					new ArrayBlockingQueue<>(1024), new RenameThreadFactory(ThreadConstants.BULID_SETTLE_DETAIL_POOL));
		return settleMerchantNoThreadPool;

	}
	
	@Bean(value = "querySettleDataPageThreadPool")
	public ExecutorService queryPageMerchantNoThreadPool( ) {
		int cpuCount = Runtime.getRuntime().availableProcessors();
		ExecutorService querySettleDataPageThreadPool = new ThreadPoolExecutor(cpuCount +1, cpuCount +1, 60L, TimeUnit.SECONDS,
				new LinkedBlockingQueue<>(1024), new RenameThreadFactory(ThreadConstants.QUERY_SETTLE_DATA_PAGE_POOL));
		return querySettleDataPageThreadPool;
	}
}

线程名规则重写:

package com.XXXX.merchant.service.task;

import com.XXXX.merchant.util.constants.ThreadConstants;
import org.apache.commons.lang3.StringUtils;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>Title: RenameThreadFactory.java</p>
 * <p>Description: 线程分组重命名工厂</p>
 * <p>Copyright: Copyright (c) 2017 XXXX技术有限公司</p>
 * <p>Company: XXXX技术有限公司</p>
 * <p>Author:xxxx</p>
 * <p>CreateTime:2019年11月27日上午11:37:38
 */
public class RenameThreadFactory implements ThreadFactory{
	
	// 线程组
	private final String prefix;
	// 守护线程标识
    private final boolean daemoThread;
    private final ThreadGroup threadGroup;
    // 默认组序号
    private final static AtomicInteger defaultGroupThreadNumber=new AtomicInteger(1);
    // 个性化组线程序号
    private final AtomicInteger personalizedThreadNum = new AtomicInteger(1);
	
	@Override
	public Thread newThread( Runnable r ) {
		String name = prefix + personalizedThreadNum.getAndIncrement();
        Thread thread = new Thread(threadGroup, r, name, 0);
        thread.setDaemon(daemoThread);
        return thread;
	}

	public RenameThreadFactory ( ) {
		this(ThreadConstants.DEFAULT_PREFIX , false);
	}
	
	public RenameThreadFactory(String prefix) {
		this(prefix, false);
	}
	
	public RenameThreadFactory ( String prefix , boolean daemo ) {
		this.prefix = (StringUtils.isNotEmpty(prefix) ? prefix
				: ThreadConstants.DEFAULT_PREFIX) + defaultGroupThreadNumber.getAndIncrement() + ThreadConstants.THREAD_SPLIT;
		daemoThread = daemo;
		SecurityManager s = System.getSecurityManager();
		threadGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
	}
}

多商户情况下的商户线程以及某一商户2w写一个线程

package com.XXXX.merchant.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.XXXX.merchant.dal.model.ClearingStatementEnum;
import com.XXXX.merchant.service.ClearingStatementService;
import com.XXXX.merchant.service.UploadSettleBillService;
import com.XXXX.merchant.service.task.ClearingSettleQueryTask;
import com.XXXX.merchant.util.DateUtil;
import com.XXXX.merchant.util.ExcelUtil;
import com.XXXX.merchant.util.ExportExcelZipUtils;
import com.XXXX.merchant.util.FtpUtil;
import com.XXXX.merchant.util.bean.domain.UploadSettleBill;
import com.XXXX.merchant.util.constants.Constants;
import com.XXXX.merchant.util.enums.EnumBase;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>Title: UploadSettleBillServiceImpl.java</p>
 * <p>Description: 上传结算单至ftp实现类</p>
 * <p>Copyright: Copyright (c) 2017XXXX技术有限公司</p>
 * <p>Company:XXXX技术有限公司</p>
 * <p>Author:xxxx</p>
 * <p>CreateTime:2019年11月27日下午2:38:44
 */
@Service
@Slf4j
public class UploadSettleBillServiceImpl implements UploadSettleBillService {

	@Resource
	ClearingStatementService clearingStatementService;

	@Resource(name = "settleMerchantNoThreadPool")
	private ExecutorService settleMerchantNoThreadPool;
	@Resource(name = "querySettleDataPageThreadPool")
	private ExecutorService querySettleDataPageThreadPool;

	@Value("${merchant.excel.tempPath}")
	private String tempPath;

	@Resource
	FtpUtil ftpUtil;

	@Resource
	RedisUtil redisUtil;

	@Override
	public boolean bulidClearingSettleData( UploadSettleBill query ) {
		try {
			Date clearingDate = query.getClearingDate();
			if (null == clearingDate) {
				clearingDate = DateUtil.parser(DateUtil.getZoneTimeDate(new Date(), ((8 * 60) - (Constants.GMT_NO * 60))),
						DateUtil.POINT_DATA_LONG_DTM_PART_FORMAT);
			}
			List< String > merchantNoList = query.getMerchantNoList();
			if (CollectionUtils.isEmpty(merchantNoList)) {
				merchantNoList = clearingStatementService.getDistanceMerchantNoList(clearingDate);
				if (CollectionUtils.isEmpty(merchantNoList)) {
					log.info("merchantNoList is null,can not bulid clearing settle data!");
					return Boolean.TRUE;
				}
			}
			final Date queryDate = clearingDate;
			merchantNoList.stream().forEach(merchantNo -> settleMerchantNoThreadPool.execute(( ) -> {
				int lockFlag = redisUtil.lockIgnoreException(CachePrefixUtil.BULID_CLEARING_SETTLE+merchantNo+ Constants.EXCEL_SPLIT+queryDate, CachePrefixUtil.ONE_HOUR * 3);
				if(RedisUtil.LOCK_STATUS_SUCCESS == lockFlag) {
					String year = DateUtil.getYear();
					String month = DateUtil.getMonth(queryDate);
					String day = DateUtil.getDay();// 补跑产生的多线程delAllFile 问题
					String execlTemp = tempPath + File.separator + Constants.BILL_TYPE_SETTLE + File.separator + year + File.separator
							+ month + File.separator +day + File.separator + merchantNo;

					int total = clearingStatementService.queryClearingStatementPages(merchantNo, queryDate);
					if (0 == total) {
						log.info("{} settle data no record in {}", merchantNo, queryDate);
						return;
					}
					int tempLastOne = total % Constants.EXCEL_SIZE_COUNT;
					int countTimes = total / Constants.EXCEL_SIZE_COUNT;
					int queryClearTimes = (0 == tempLastOne) ? countTimes : countTimes + 1;
					try {
						LinkedBlockingQueue< String > runQueue = new LinkedBlockingQueue< String >(queryClearTimes);
						List< String > fileNames = new ArrayList< String >();
						CompletionService< String > completionService = null;
						for (int i = 0; i < queryClearTimes; i++) {
							if(i == (queryClearTimes-1) && tempLastOne != 0) {
								total = tempLastOne;
							}
							completionService = new ExecutorCompletionService(querySettleDataPageThreadPool, runQueue);
							completionService.submit(new ClearingSettleQueryTask(merchantNo, queryDate, i, execlTemp,total));
						}
						for (int i = 0; i < queryClearTimes; i++) {
							String response = completionService.take().get();
							fileNames.add(response);
						}
						String targetZip = Constants.FTP_FILE_ZIP_PREFIX + Constants.EXCEL_SPLIT + merchantNo + Constants.EXCEL_SPLIT
								+ DateUtil.toMailDateDtPartString(queryDate) + Constants.FTP_FILE_ZIP_SUFFIX;
						String targetZipPath = execlTemp + File.separator + targetZip;
						ExportExcelZipUtils.toZipFiles(targetZipPath, fileNames);
						InputStream is = new FileInputStream(targetZipPath);
						ftpUtil.uploadClearingSettleFile(year, month, merchantNo, Constants.CLEARING_SETTLE_REPORT, targetZip, is);
					} catch (ExecutionException e) {
						log.error("bulidClearingSettleData ExecutionException e", e);
					} catch (InterruptedException e) {
						log.error("bulidClearingSettleData InterruptedException e", e);
					} catch (FileNotFoundException e) {
						log.error("bulidClearingSettleData FileNotFoundException e", e);
					}finally {
						ExportExcelZipUtils.delAllFile(execlTemp);
						redisUtil.unLockIgnoreException(CachePrefixUtil.BULID_CLEARING_SETTLE+merchantNo+ Constants.EXCEL_SPLIT+queryDate);
					}
				}else {
					log.info("merchantNo : {} in date {} get lock failed, please try later again", merchantNo, queryDate);
				}
				
			}));
			 settleMerchantNoThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
		} catch (ParseException e) {
			log.error("set default query date error!", e);
			return Boolean.FALSE;
		} catch (Exception e) {
			log.error("settle rollback error!", e);
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}
	
	/**
	 * D:\application\merchant\temp\settle\2019\12\200000040001 下生成零时文件
	 * @param merchantNo
	 * @param queryDate
	 * @param dataList
	 * @param time
	 * @param path
	 * @param pageTime
	 * @return
	 */
	@Override
	public String createExcel( String merchantNo, Date queryDate, List< ClearingStatementEnum > dataList, int time,String path, int pageTime ) {
		ClearingStatementEnum clearingStatement = new ClearingStatementEnum();
		String columnName = "";
		String mailDateDtPartString = DateUtil.toMailDateDtPartString(queryDate);
		String[] titles = ExcelUtil.settleDetailTitle;
		final List< String[] > datas = new ArrayList<>();
		for (int k = 0; k < dataList.size(); k++) {
			clearingStatement = dataList.get(k);
			final String[] data = new String[titles.length];
			for (int j = 0; j < titles.length; j++) {
				/**
				 * 反射得到当前列的值
				 */
				String value = null;
				try {
					Method getMethod = ClearingStatementEnum.class
							.getMethod(ExcelUtil.COLUNM_METHOD_CLEARINGSTATEMENT.get(titles[j]));
					columnName = ExcelUtil.COLUNM_METHOD_CLEARINGSTATEMENT.get(titles[j]);
					if (StringUtils.equals(columnName, ExcelUtil.COLUNM_METHOD_CLEARINGSTATEMENT.get(titles[4]))) {
						value = DateUtil.toPointDtmPart((Date) getMethod.invoke(clearingStatement));
					} else if (StringUtils.equalsAny(columnName, ExcelUtil.COLUNM_METHOD_CLEARINGSTATEMENT.get(titles[6]),
							ExcelUtil.COLUNM_METHOD_CLEARINGSTATEMENT.get(titles[7]))) {
						value = ((EnumBase) getMethod.invoke(clearingStatement)).getValue();
					} else {
						value = (String) getMethod.invoke(clearingStatement);
					}
					data[j] = value;
				} catch (Exception e) {
					log.error("ClearingStatementEnum have not method {}",
							ExcelUtil.COLUNM_METHOD_CLEARINGSTATEMENT.get(titles[j]));
				}
			}
			datas.add(data);
		}
		String fileName = Constants.FTP_FILE_PREFIX + Constants.EXCEL_SPLIT + merchantNo + Constants.EXCEL_SPLIT
				+ mailDateDtPartString + Constants.EXCEL_SPLIT + ++time + Constants.EXCEL_FILE_SUFFIX;
		return ExportExcelZipUtils.excelToLocalForPage(titles, datas, path, fileName,pageTime);
	}

}

注意:目前目录没有到日只到月,线程补跑和当日线程同时执行情况下存在多线程问题,ExportExcelZipUtils.delAllFile(execlTemp);会出现问题。

 

String day = DateUtil.getDay();// 补跑产生的多线程delAllFile 问题
                String execlTemp = tempPath + File.separator + Constants.BILL_TYPE_SETTLE + File.separator + year + File.separator
                        + month + File.separator +day + File.separator + merchantNo;

同一天多线程 创建相同的文件名 的问题 需要通过分布式锁解决

2.5w一个excel的写线程

package com.XXXX.merchant.service.task;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.XXXX.merchant.dal.model.ClearingStatementEnum;
import com.XXXX.merchant.service.context.MyContextHodler;
import com.XXXX.merchant.service.impl.ClearingStatementServiceImpl;
import com.XXXX.merchant.service.impl.UploadSettleBillServiceImpl;
import com.XXXX.merchant.util.constants.Constants;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>Title: ClearingSettleQueryTask.java</p>
 * <p>Description: 分页读取数据库数据</p>
 * <p>Copyright: Copyright (c) 2017 XXXX技术有限公司</p>
 * <p>Company: XXXX技术有限公司</p>
 * <p>Author:xxxx</p>
 * <p>CreateTime:2019年12月4日下午3:28:59
 */
@Slf4j
public class ClearingSettleQueryTask implements Callable< String >{
	private String merchantNo;
	private Date queryDate;
	private int time;
	private String path;
	private int total;

	
	public ClearingSettleQueryTask ( ) {}

	public ClearingSettleQueryTask ( String merchantNo , Date queryDate , int time, String path, int total ) {
		this.merchantNo = merchantNo;
		this.queryDate = queryDate;
		this.time = time;
		this.path = path;
		this.total = total;
	}

	@Override
	public String call( ) throws Exception {
		String fileName = "";
		try {
			int temp = (total > Constants.EXCEL_SIZE_COUNT) ? Constants.EXCEL_SIZE_COUNT : total;
			int index = temp % Constants.CLEAR_SETTLE_SIZE;
			int countTimes = temp / Constants.CLEAR_SETTLE_SIZE;
			int queryIndex = (index == 0) ? countTimes : countTimes + 1;
			List< ClearingStatementEnum > dataList = new ArrayList< ClearingStatementEnum >();
			ClearingStatementServiceImpl clearingStatementService = (ClearingStatementServiceImpl) MyContextHodler
					.getBean("clearingStatementServiceImpl");
			UploadSettleBillServiceImpl uploadSettleBillService = (UploadSettleBillServiceImpl) MyContextHodler
					.getBean("uploadSettleBillServiceImpl");
			int pageSize = Constants.CLEAR_SETTLE_SIZE;
			for (int i = 0; i < queryIndex; i++) {
				if (i == (queryIndex - 1)) {
					pageSize = index;
				}
				dataList = clearingStatementService.getClearingSettleForExcel(merchantNo, queryDate,
						time * Constants.EXCEL_SIZE_COUNT + i * Constants.CLEAR_SETTLE_SIZE, pageSize);
				if (null != dataList) {
					fileName = uploadSettleBillService.createExcel(merchantNo, queryDate, dataList, time, path, i);
				}
			}
		} catch (Exception e) {
			log.error("{} query settle data exception in {}", merchantNo, queryDate, e);
		}
		//System.out.println("time:"+time+"======total:"+total);
		return fileName;
	}


	public String getMerchantNo( ) {
		return merchantNo;
	}

	public void setMerchantNo( String merchantNo ) {
		this.merchantNo = merchantNo;
	}

	public Date getQueryDate( ) {
		return queryDate;
	}

	public void setQueryDate( Date queryDate ) {
		this.queryDate = queryDate;
	}

	public int getTime( ) {
		return time;
	}

	public void setTime( int time ) {
		this.time = time;
	}

	public String getPath( ) {
		return path;
	}

	public void setPath( String path ) {
		this.path = path;
	}

	public int getTotal( ) {
		return total;
	}

	public void setTotal( int total ) {
		this.total = total;
	}

	
}

Excel表头和反射字段配置

package com.XXXX.merchant.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import com.XXXX.merchant.util.bean.request.ExportTradeTitle;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ExcelUtil {

    public static final String[] settleDetailTitle ={"Merchant Name","Merchant ID",......,"Record Type","Payment Currency","Payment Amount","Settlement Currency","Income","Expenses","Transaction fee",......};
    

    
    public static HashMap<String,String> COLUNM_METHOD_CLEARINGSTATEMENT = new HashMap<String,String>();

    static{
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Merchant Name", "getMerchantName");
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Merchant ID", "getMerchantId");
    	......
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Record Type", "getRecordType");
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Payment Currency", "getPaymentCurrency");
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Payment Amount", "getPaymentAmount");
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Settlement Currency", "getClearingCurrency");
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Income", "getIncome");
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Expenses", "getExpenses");
    	COLUNM_METHOD_CLEARINGSTATEMENT.put("Transaction fee", "getTransactionFee");
    	......
    }
}

excel基础类

package com.XXXX.merchant.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jp.merchant.util.constants.Constants;
import com.opensymphony.webwork.ServletActionContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import sun.applet.Main;

/**
 * <p>Title: ClearingSettleQueryTask.java</p>
 * <p>Description: Excel基类</p>
 * <p>Copyright: Copyright (c) 2017 XXXX技术有限公司</p>
 * <p>Company: XXXX技术有限公司</p>
 * <p>Author:xxxx</p>
 * <p>CreateTime:2019年12月4日下午3:28:59
 */
@Slf4j
public class ExportExcelZipUtils {


   ......

	public static String toZipFiles( String execlTemp, List< String > fileNameList ) {
		final File[] files = new File[fileNameList.size()];
        for (int i = 0; i < fileNameList.size(); i++) {
            files[i] = new File(fileNameList.get(i));
        }
        // 压缩文件
        final File zipFile = new File(execlTemp);
        // 将excel文件压缩成zip文件
        final byte[] buf = new byte[1024];
        ZipOutputStream zipOut = null;
        FileInputStream fis = null;

        try {
            zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
            for (int i = 0; i < files.length; i++) {
                fis = new FileInputStream(files[i]);
                zipOut.putNextEntry(new ZipEntry(files[i].getName()));
                int len = 0;
                while ((len = fis.read(buf)) > 0) {
                    zipOut.write(buf, 0, len);
                }
                zipOut.closeEntry();
                fis.close();
            }
        } catch (final Exception e) {
        	execlTemp = null;
            log.error("failed to create zip file");
        } finally {
            if (zipOut != null) {
                try {
                    zipOut.close();
                } catch (final IOException e) {
                    log.error("failed to close ZipOutputStream");
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (final IOException e) {
                    log.error("failed to close FileInputStream");
                }
            }
        }
        return execlTemp;
	}



    /**
     *  删除文件夹下的所有文件
     * @param path
     * @return
     */
    public static boolean delAllFile(String path) {
        boolean flag = false;
        File file = new File(path);
        if (!file.exists()) {
            return flag;
        }
        if (!file.isDirectory()) {
            return flag;
        }
        String[] tempList = file.list();
        File temp = null;
        for (int i = 0; i < tempList.length; i++) {
            if (path.endsWith(File.separator)) {
                temp = new File(path + tempList[i]);
            } else {
                temp = new File(path + File.separator + tempList[i]);
        }
            if (temp.isFile()) {
                temp.delete();
            }
            if (temp.isDirectory()) {
                delAllFile(path + File.separator + tempList[i]);//先删除文件夹里面的文件
                delFolder(path + File.separator + tempList[i]);//再删除空文件夹
                flag = true;
            }
        }
        return flag;
    }


	public static String excelToLocalForPage( String[] titles, List< String[] > datas, String path, String fileName, int pageTime ) {
		log.info("excelToLocalForPage to local");
		String finalFileName = path + File.separator + fileName;

		Workbook workbook = null;
        Sheet sheet = null;
        Row row = null;
        OutputStream fos = null;
        try {
            File temp = new File(path);
            if (!temp.exists()){
                temp.mkdirs();
            }
            
            if(pageTime == 0){
            	workbook = new HSSFWorkbook();
                sheet = workbook.createSheet("sheet1");
                row = sheet.createRow(0);
                for (int i = 0; i < titles.length; i++) {
                    row.createCell(i).setCellValue(titles[i]);
                }
            }else {
            	POIFSFileSystem poifsFileSystem=new POIFSFileSystem(new FileInputStream(finalFileName));
            	workbook = new HSSFWorkbook(poifsFileSystem);
            	sheet = workbook.getSheet("sheet1");
            }
            int startPage = pageTime*Constants. CLEAR_SETTLE_SIZE;
            for (int i = 0; i < datas.size(); i++) {
            
                final String[] vals = datas.get(i);
                row = sheet.createRow(startPage + 1 +i);
                for (int j = 0; j < vals.length; j++) {
                    row.createCell(j).setCellValue(vals[j]);
                }
            }

            fos = new FileOutputStream(finalFileName);
            workbook.write(fos);
            fos.flush();
        } catch (final Exception e) {
            log.error("create excel Exception e",e);
        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (final IOException e) {
                    log.error("workbook关闭失败", e);
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (final IOException e) {
                    log.error("输出流关闭失败", e);
                }
            }
        }
		return finalFileName;
	}

	
}

ftp上传下载

package com.XXXX.merchant.util;

import com.XXXX.merchant.util.constants.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.SocketException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * <p>Title: UploadSettleBillServiceImpl.java</p>
 * <p>Description: ftp基类</p>
 * <p>Copyright: Copyright (c) 2017 XXXX技术有限公司</p>
 * <p>Company: XXXX技术有限公司</p>
 * <p>Author:xxxx</p>
 * <p>CreateTime:2019年11月27日下午2:38:44
 */
@Component
@Slf4j
public class FtpUtil {

	@Value("${merchant.ftp.host}")
	private String ftpHost;

	@Value("${merchant.ftp.userName}")
	private String ftpUserName;

	@Value("${merchant.ftp.password}")
	private String ftpPassword;

	@Value("${merchant.ftp.port}")
	private String port;

	/**
	 * 获取FTPClient对象
	 * @return
	 */
	public FTPClient getFTPClient( ) {
		FTPClient ftpClient = new FTPClient();
		try {
			ftpClient = new FTPClient();
			ftpClient.connect(ftpHost, Integer.parseInt(port));// 连接FTP服务器
			ftpClient.login(ftpUserName, ftpPassword);// 登陆FTP服务器
			if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
				log.info("未连接到FTP,用户名或密码错误。");
				ftpClient.disconnect();
			} else {
				log.info("FTP连接成功。");
			}
		} catch (SocketException e) {
			log.error("FTP的IP地址可能错误,请正确配置。");
		} catch (IOException e) {
			log.error("FTP的端口错误,请正确配置。");
		}
		return ftpClient;
	}

	public boolean uploadClearingSettleFile( String year, String month, String merchantNo, String bussType, String fileName,
			InputStream is ) {
		boolean success = false;
		FTPClient ftpClient = null;
		InputStream oldIs = null;
		try {
			int reply;
			ftpClient = getFTPClient();
			reply = ftpClient.getReplyCode();
			if (!FTPReply.isPositiveCompletion(reply)) {
				ftpClient.disconnect();
				return success;
			}
			ftpClient.setControlEncoding("UTF-8"); // 中文支持
			ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
			ftpClient.enterLocalPassiveMode();
			ftpClient.makeDirectory(Constants.FTP_TOP_PATH);
			ftpClient.changeWorkingDirectory(Constants.FTP_TOP_PATH);
			ftpClient.makeDirectory(merchantNo);
			ftpClient.changeWorkingDirectory(merchantNo);
			ftpClient.makeDirectory(year);
			ftpClient.changeWorkingDirectory(year);
			ftpClient.makeDirectory(month);
			ftpClient.changeWorkingDirectory(month);
			ftpClient.makeDirectory(bussType);
			ftpClient.changeWorkingDirectory(bussType);
			// 判断文件是否已经存在
			oldIs = ftpClient.retrieveFileStream(fileName);
			if (oldIs != null && ftpClient.getReplyCode() != FTPReply.FILE_UNAVAILABLE) {
				log.info("fileName : {} has existed,need delete then reStore!", fileName);
				ftpClient.deleteFile(fileName);
			}
			ftpClient.storeFile(fileName, is);

			ftpClient.logout();
			success = true;
		} catch (IOException e) {
			log.error("uploadFile IOException e", e);
		} finally {
			try {
				if (null != is) {
					is.close();
				}
				if (null != oldIs) {
					oldIs.close();
				}
				if (null != ftpClient && ftpClient.isConnected()) {
					ftpClient.disconnect();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return success;
	}

	public void downloadZipFtp( String ftpPath, OutputStream outputStream ) throws FileNotFoundException {
		FTPClient ftpClient = null;
		try {
			ftpClient = getFTPClient();
			// 中文支持
			ftpClient.setControlEncoding("UTF-8");
			ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
			ftpClient.enterLocalPassiveMode();
			ftpClient.changeWorkingDirectory(ftpPath);
			FTPFile[] ftpFiles = ftpClient.listFiles();
			if (ftpFiles == null || ftpFiles.length == 0) {
				throw new FileNotFoundException("ftpPath:【" + ftpPath + "】 have no file in this path");
			}
			ZipOutputStream zipOut = null;
			InputStream inputStream = null;
			final byte[] buf = new byte[1024];
			try {
				zipOut = new ZipOutputStream(outputStream);
				for (FTPFile ftpFile : ftpFiles) {
					zipOut.putNextEntry(new ZipEntry(ftpFile.getName()));
					if (ftpFile.isFile()) {
						inputStream = ftpClient.retrieveFileStream(ftpFile.getName());
						int len = 0;
						while ((len = inputStream.read(buf)) > 0) {
							zipOut.write(buf, 0, len);
						}
						zipOut.closeEntry();
						inputStream.close();
					}
				}
			} catch (final Exception e) {
				log.error("failed to create zip file");
			} finally {
				if (zipOut != null) {
					try {
						zipOut.close();
					} catch (final IOException e) {
						log.error("failed to close ZipOutputStream");
					}
				}
				if (inputStream != null) {
					try {
						inputStream.close();
					} catch (final IOException e) {
						log.error("failed to close FileInputStream");
					}
				}
				ftpClient.logout();
			}
		} catch (FileNotFoundException e) {
			log.error("没有找到" + ftpPath + "文件");
			throw e;
		} catch (SocketException e) {
			log.error("连接FTP失败.");
		} catch (IOException e) {
			log.error("文件读取错误。");
		}
	}

}

int total 数据不共享, 如果对象类型共享(merchantNo )我们可以通过ThreadLocal task使用

调试过程中日志:

time:4=====i:0===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
time:0=====i:0===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
time:2=====i:0===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
time:3=====i:0===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.330 [query-page-settle-detail-pool-3-thread-5] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.334 [query-page-settle-detail-pool-3-thread-4] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
time:1=====i:0===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.337 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.340 [query-page-settle-detail-pool-3-thread-1] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.425 [query-page-settle-detail-pool-3-thread-3] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.636 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.636 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 7, 2;] cost 9 ms;
time:1=====i:1===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.637 [query-page-settle-detail-pool-3-thread-3] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.637 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 12, 2;] cost 11 ms;
2019-12-06 14:10:14.637 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
time:2=====i:1===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.638 [query-page-settle-detail-pool-3-thread-3] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.657 [query-page-settle-detail-pool-3-thread-5] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.656 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 22, 2;] cost 27 ms;
2019-12-06 14:10:14.658 [query-page-settle-detail-pool-3-thread-4] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.658 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 17, 2;] cost 31 ms;
time:3=====i:1===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.661 [query-page-settle-detail-pool-3-thread-4] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
time:4=====i:1===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.664 [query-page-settle-detail-pool-3-thread-5] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.675 [query-page-settle-detail-pool-3-thread-1] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.675 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 2, 2;] cost 45 ms;
time:0=====i:1===queryData:[{"transactionFee":"0.12","updateTime":1575597760000},{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.676 [query-page-settle-detail-pool-3-thread-1] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.759 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.759 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 9, 1;] cost 17 ms;
time:1=====i:2===queryData:[{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.759 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.762 [query-page-settle-detail-pool-3-thread-1] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.762 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 4, 1;] cost 16 ms;
time:0=====i:2===queryData:[{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.763 [query-page-settle-detail-pool-3-thread-1] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.769 [query-page-settle-detail-pool-3-thread-3] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.769 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 14, 1;] cost 19 ms;
time:2=====i:2===queryData:[{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.770 [query-page-settle-detail-pool-3-thread-3] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.780 [query-page-settle-detail-pool-3-thread-4] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.780 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 19, 1;] cost 22 ms;
time:3=====i:2===queryData:[{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.780 [query-page-settle-detail-pool-3-thread-4] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 14:10:14.794 [query-page-settle-detail-pool-3-thread-5] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:10:14.794 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 24, 1;] cost 39 ms;
time:4=====i:2===queryData:[{"transactionFee":"0.12","updateTime":1575597760000}]
2019-12-06 14:10:14.795 [query-page-settle-detail-pool-3-thread-5] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local

i:0,response:D:\application_server\XXXX-merchant\temp\settle\2019\12\110120119\Settlement_110120119_20191206_3.xls
i:1,response:D:\application_server\XXXX-merchant\temp\settle\2019\12\110120119\Settlement_110120119_20191206_4.xls
2019-12-06 14:59:28.996 [query-page-settle-detail-pool-3-thread-5] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:59:28.996 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 24, 1;] cost 26 ms;
2019-12-06 14:59:28.997 [query-page-settle-detail-pool-3-thread-5] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
i:2,response:D:\application_server\XXXX-merchant\temp\settle\2019\12\110120119\Settlement_110120119_20191206_5.xls
2019-12-06 14:59:29.002 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:59:29.002 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 9, 1;] cost 20 ms;
2019-12-06 14:59:29.003 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
i:3,response:D:\application_server\XXXX-merchant\temp\settle\2019\12\110120119\Settlement_110120119_20191206_2.xls
2019-12-06 14:59:29.009 [query-page-settle-detail-pool-3-thread-1] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 14:59:29.009 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 4, 1;] cost 15 ms;
2019-12-06 14:59:29.010 [query-page-settle-detail-pool-3-thread-1] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
i:4,response:D:\application_server\XXXX-merchant\temp\settle\2019\12\110120119\Settlement_110120119_20191206_1.xls
2019-12-06 14:59:29.092 [bulid-settle-detail-pool-2-thread-1] INFO  [com.XXXX.merchant.util.FtpUtil:46]- FTP连接成功。


int total 数据不共享, 如果对象类型共享我们可以通过ThreadLocal task使用
2019-12-06 16:51:49.965 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
time:1======total:25
time:2======total:25
time:3======total:25
2019-12-06 16:51:50.032 [query-page-settle-detail-pool-3-thread-3] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 16:51:50.032 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 24, 1;] cost 12 ms;
time:4======total:25
2019-12-06 16:51:50.033 [query-page-settle-detail-pool-3-thread-3] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
time:6======total:1
time:0======total:25
2019-12-06 16:51:50.043 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 16:51:50.043 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 20, 2;] cost 24 ms;
2019-12-06 16:51:50.044 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
2019-12-06 16:51:50.052 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.dal.util.MyBatisSqlInterceptor:56]- 2019-12-06 16:51:50.052 com.XXXX.merchant.dal.dao.xxMapper.getClearingSettleForExcel [select * from merchant_settle_detail where CLEARING_DATE = STR_TO_DATE('2019-12-06 00:00:00','%Y-%m-%d') and MERCHANT_ID = '110120119' order BY CLEARING_TIME desc limit 22, 0;] cost 6 ms;
2019-12-06 16:51:50.053 [query-page-settle-detail-pool-3-thread-2] INFO  [com.XXXX.merchant.util.ExportExcelZipUtils:377]- excelToLocalForPage to local
time:5======total:25

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值