Java下载文件使用流式文件处理 分块传输(2G)文件,备份Mysqls数据库

由于备份的文件都比较大,开始就碰到了问题,前端报错了 504 Gateway Time-out

1.增加nginx 超时时间:增加 proxy_read_timeoutproxy_connect_timeout 的值,以允许更多的时间来等待后端服务的响应

2.分块传输

3.流式处理

部分优化后代码:
/**
 * @author xyt
 */
@RestController
@RequestMapping("/system")
@Slf4j
public class GlobalSystemController {
    @Resource
    private ISystemService systemService;
    @ApiOperation(value = "全量备份系统,带历史数据")
    @GetMapping("/backup")
    @OpLog(outputExpression = "{#code}")
    public void backupSystem(HttpServletResponse response){
        // 异步备份
        CompletableFuture<File> backupFuture = systemService.backupSystemAsync();
        // 等待备份完成并获取文件
        File file = backupFuture.join();
        if (file == null) return;
        // 设置响应头
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
        response.setHeader("Transfer-Encoding", "chunked");
        // 使用流式处理下载文件
        try (FileInputStream fileInputStream = new FileInputStream(file);
             OutputStream outputStream = response.getOutputStream()) {
            //从输入流到输出流进行拷贝,适用于大文件
            IOUtils.copyLarge(fileInputStream, outputStream);
            log.info("系统文件{}已下载!", file.getName());
        } catch (IOException e) {
            log.error("下载失败: {}", e.getMessage());
        } finally {
            // 异步删除文件
            CompletableFuture.runAsync(() -> FileOperationUtil.deleteFile(file.getAbsolutePath()));
        }
    }
}
    public File backupSystem() throws Exception {
        String project = workSpaceConfig.getConfig();
        SystemInfoDTO systemInfo = systemInfoService.getSystemInfo();
        String name = systemInfo.getName();
        //压缩文件临时存放地址
        String destPath = new File(project).getParent() + File.separator + name + "配置备份(window).zip";
        File destFile = new File(destPath);
        File srcFile = new File(project);
        try {
            //存放导出mysql 脚本目录
            String backDBConfigShellPath = project + File.separator + "shell" + File.separator + "backup_db.bat";
            // 备份出的sql
            RunTimeUtil.runCmd(new String[]{backDBConfigShellPath});
        } catch (Exception e) {
            e.printStackTrace();
            log.error("备份sql文件失败:{}", e.getMessage());
        } finally {
            Thread.sleep(500);
        }
        // 创建压缩文件
        ZipUtil.zip(destFile, Charset.forName("UTF8"), false, item -> true, srcFile);
        return destFile;
    }
​
    @Override
    public CompletableFuture<File> backupSystemAsync() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return backupSystem();
            } catch (Exception e) {
                log.error("全量备份失败: {}", e.getMessage());
                return null;
            }
        });
    }

对于处理大文件(例如2GB的ZIP文件)时,超时和资源管理是常见的问题:

  1. 分块传输:使用分块传输(chunked transfer encoding)来处理大文件,可以避免一次性加载整个文件到内存中。

  2. 流式处理:确保文件处理是流式的,而不是一次性读取整个文件到内存中。

  3. 异步处理:确保文件压缩和下载过程都是异步的,避免阻塞主线程。

  4. 增加超时时间:调整服务器和客户端的超时时间设置,以应对大文件传输所需的时间。

  5. 使用更高效的I/O库:例如NIO(非阻塞I/O)可以提高文件读写的效率。


导出mysql脚本:

backup_db.bat(username , pwd, port, dbName 修改为自己的数据库信息)

@echo off
set currPath=%~dp0
set split=''
:begin
FOR /F "tokens=1,* delims=\" %%i IN ("%currPath%")  DO (set split=%%i)
FOR /F "tokens=1,* delims=\" %%i IN ("%currPath%")  DO (set currPath=%%j)
if not "%parentPath%" == "" goto gotJpdaOpts
set parentPath=%split%\
goto begin
:gotJpdaOpts
if %parentPath%%split%\==%~dp0 goto end
set parentPath=%parentPath%%split%\
goto begin
:end
​
set username=数据库用户
set pwd=数据库密码
set port=3306
set dbName=数据库名   
set sqlPath=%parentPath%%dbName%.sql
mysql -u%username% -p%pwd% %dbName% -s -e "call clean_all_data()"
mysqldump -u%username% -p%pwd% -P%port% --default-character-set=utf8mb4 --lock-tables  --extended-insert=false --triggers --events -R --hex-blob --single-transaction   %dbName% > %sqlPath%
echo sql save path  %sqlPath%

接口地址 : "/project/system/backup"

Nginx优化(调整服务器和客户端的超时时间设置,以应对大文件传输所需的时间):
server {
    listen 80;  # 或者你需要的端口
    server_name 127.0.0.1;  # 替换为你的域名
    
    client_max_body_size 500m;
    client_header_timeout 30;
    client_body_timeout 30;
    
    location /api/ {
        proxy_pass http://localhost:9527/;
        proxy_connect_timeout 60s;
        proxy_read_timeout 120s;
        proxy_send_timeout 120s;
    }
​
    location /project/system/backup {
        proxy_pass http://localhost:9527;  # 确保目标地址是正确的
        proxy_read_timeout 300;     # 从后端服务器读取响应的超时时间
        proxy_connect_timeout 300;  # 与后端服务器建立连接的超时时间
        proxy_send_timeout 300;     # 向后端服务器发送请求的超时时间
    }
    ...
}

说明:

  1. proxy_pass: 指定要将请求转发到的后端服务地址。

  2. proxy_read_timeout: 设置从后端服务器读取响应的超时时间。

  3. proxy_connect_timeout: 设置与后端服务器建立连接的超时时间。

  4. proxy_send_timeout: 设置向后端服务器发送请求的超时时间。

  5. proxy_set_header: 这些设置用于传递请求的头部信息,确保后端能够获取到客户端的真实信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值