1. 程式人生 > 實用技巧 >多執行緒讀表-壓縮成zip下載

多執行緒讀表-壓縮成zip下載

1、背景介紹

  讀13張表,4000條放到一個excel,打包成zip,並加密下載。本文為Demo版本,實現了多執行緒匯出excel並打包zip提供下載,沒有實現每4000條放到一個zip中以及zip加密。

2、pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.22</version>
        </dependency>
        <!--MySQL資料庫驅動-->
        <dependency>
            <groupId>javax.jms</groupId>
            <artifactId>javax.jms-api</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.38</version>
        </dependency>
        <!-- json schema 轉換 fge -->
        <dependency>
            <groupId>com.github.fge</groupId>
            <artifactId>json-schema-validator</artifactId>
            <version>2.2.6</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-support</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>
    </dependencies>

2、config包下多執行緒配置類

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class ExecutorConfig {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
    @Value("${task.pool.corePoolSize}")
    private int corePoolSize;
    @Value("${task.pool.maxPoolSize}")
    private int maxPoolSize;
    @Value("${task.pool.keepAliveSeconds}")
    private int keepAliveSeconds;
    @Value("${task.pool.queueCapacity}")
    private int queueCapacity;
    @Value("${task.pool.threadNamePrefix}")
    private String threadNamePrefix;
 
    @Bean
    public Executor asyncExcelServiceExecutor() {
        logger.info("...ExecutorConfig...asyncServiceExecutor()...啟動[zip任務]執行緒池...");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setThreadNamePrefix(threadNamePrefix);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

3、application-dev.properties多執行緒設定

task.pool.corePoolSize=15
task.pool.maxPoolSize=99
task.pool.keepAliveSeconds=300
task.pool.queueCapacity=999
task.pool.threadNamePrefix=Grape-

4、開啟多執行緒

@EnableAsync
@SpringBootApplication
@MapperScan(basePackages = {"....ry.dao"})
public class BasicSystem {
    public static void main(String[] args) {
        SpringApplication.run(BasicSystem.class, args);
    }
}

5、service層方法

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
import java.util.zip.ZipOutputStream;

@Service
public class NOnij {
    @Autowired
    private NOnijAsync nOnijAsync;

    public void zip(HttpServletResponse response) throws IOException {
        response.setContentType("application/force-download");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment; filename=" + new String(("sjn-" + DateUtils.getDateTime() + ".zip").getBytes("UTF-8"), "ISO8859-1"));
        ZipOutputStream zipout = new ZipOutputStream(response.getOutputStream());
        InputStream inputStream = null;

        try {
            //加閂
            CountDownLatch latch = new CountDownLatch(2);

            nOnijAsync.excelUser(latch, zipout, inputStream);
            nOnijAsync.excelProject(latch, zipout, inputStream);

            //等待N個執行緒執行完畢
            latch.await();
            System.out.println("---所有執行緒---end---");

        } catch (InterruptedException e) {

        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            if (zipout != null) {
                zipout.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }
}

6、多執行緒方法

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.plugins.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Component
public class NOnijAsync {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ProductMapper productMapper;

    @Async("asyncExcelServiceExecutor")
    public void excelProject(CountDownLatch latch, ZipOutputStream zipout, InputStream inputStream) throws IOException {
        String currentThreadName = Thread.currentThread().getName();
        System.out.println("---執行緒: 【" + currentThreadName + "】 ---start---Project---");

        //分頁查詢資料,然後放到迴圈中進行寫表

        List<Product> products = productMapper.selectList(null);
        products.add(new Product("小明", "21"));
        products.add(new Product("小明", "21"));
        products.add(new Product("小明", "21"));
        products.add(new Product("小明", "21"));
        products.add(new Product("小明", "21"));

        getZip(products, zipout, inputStream, Product.class, "project");

        latch.countDown();

    }

    @Async("asyncExcelServiceExecutor")
    public Future<List<Page>> excelUser(CountDownLatch latch, ZipOutputStream zipout, InputStream inputStream) throws IOException {
        String currentThreadName = Thread.currentThread().getName();
        System.out.println("---執行緒: 【" + currentThreadName + "】 ---start---User---");

        List<User> users = userMapper.selectList(null);
        users.add(new User("小麗", "18"));
        users.add(new User("小麗", "18"));
        users.add(new User("小麗", "18"));
        users.add(new User("小麗", "18"));
        users.add(new User("小麗", "18"));
        users.add(new User("小麗", "18"));

        getZip(users, zipout, inputStream, User.class, "user");

        latch.countDown();

        return null;
    }

    private void getZip(List datas, ZipOutputStream zipout, InputStream inputStream, Class clazz, String excelName) throws IOException {
        //sheetName頁名稱
        String sheetName1 = "sheet1";
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ExcelWriter writer = EasyExcel.write(outputStream, clazz).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName1).build();
        //匯出excel
        writer.write(datas, writeSheet);
        writer.finish();
        inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        //excel檔案寫入zip
        ZipEntry zipEntry = new ZipEntry(excelName + DateUtils.getDateTime() + ".xlsx");///DateUtils.getDateTime()執行緒非安全
        zipout.putNextEntry(zipEntry);
        int len;
        byte[] buf = new byte[1024];
        while ((len = inputStream.read(buf)) > 0) {
            zipout.write(buf, 0, len);
        }
    }
}

參考:

https://blog.csdn.net/qq_35493807/article/details/105613898

參考2:

@Controller
public class TestExportZipController {
    @GetMapping("/111")
    public void export(HttpServletResponse response) throws IOException {
        response.setContentType("application/force-download");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment; filename=" + new String(("sjn-" + DateUtils.getDateTime() + ".zip").getBytes("UTF-8"), "ISO8859-1"));
        ZipOutputStream zipout = new ZipOutputStream(response.getOutputStream());
        InputStream inputStream = null;

        try {
            for (int i = 0; i < 3; i++) {
                ArrayList<UserVO> userVOList = new ArrayList<>();
                userVOList.add(new UserVO("haha", 1));
                userVOList.add(new UserVO("haha2", 12));
                userVOList.add(new UserVO("haha4", 14));
                userVOList.add(new UserVO("haha3", 13));
                userVOList.add(new UserVO("haha5", 15));

                //sheetName頁名稱
                String sheetName = "sheetName";
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ExcelWriter writer = EasyExcel.write(outputStream, UserVO.class).build();
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
                //匯出excel
                writer.write(userVOList, writeSheet);
                writer.finish();
                inputStream = new ByteArrayInputStream(outputStream.toByteArray());
                //excel檔案寫入zip
                ZipEntry zipEntry = new ZipEntry(DateUtils.getDateTime() + "-" + i + ".xlsx");
                zipout.putNextEntry(zipEntry);
                int len;
                byte[] buf = new byte[1024];
                while ((len = inputStream.read(buf)) > 0) {
                    zipout.write(buf, 0, len);
                }
            }

        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            if (zipout != null) {
                zipout.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        }

    }
}