当前位置: 首页 > article >正文

【Android】安卓 Java下载ZIP文件并解压(笔记)

写在前面的话

在这篇文章中,我们将详细讲解如何在 Android 中通过 Java 下载 ZIP 文件并解压,同时处理下载进度、错误处理以及优化方案。


以下正文


1.权限配置

在 AndroidManifest.xml 中,我们需要添加相应的权限来确保应用能够访问网络和设备存储。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

⚠️ 注意:Android 10(API 29)及以上,建议使用 getExternalFilesDir() 来处理文件,而避免直接访问 /sdcard。

2.下载 ZIP 文件并保存到本地

文件下载

首先,我们需要编写下载 ZIP 文件的代码。这里使用 HttpURLConnection 来实现文件的下载,并保存到设备的存储中。

public void downloadZipFile(String urlStr, String savePath, DownloadCallback callback) {
    new Thread(() -> {
        InputStream input = null;
        FileOutputStream output = null;
        HttpURLConnection connection = null;

        try {
            URL url = new URL(urlStr);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(10000);
            connection.connect();

            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                callback.onFailed("Server returned HTTP " + connection.getResponseCode());
                return;
            }

            int fileLength = connection.getContentLength();

            input = connection.getInputStream();
            File file = new File(savePath);
            output = new FileOutputStream(file);

            byte[] buffer = new byte[4096];
            int bytesRead;
            long total = 0;

            while ((bytesRead = input.read(buffer)) != -1) {
                total += bytesRead;
                output.write(buffer, 0, bytesRead);
                int progress = (int) (total * 100 / fileLength);
                callback.onProgress(progress);
            }

            callback.onSuccess(file.getAbsolutePath());

        } catch (Exception e) {
            e.printStackTrace();
            callback.onFailed(e.getMessage());
        } finally {
            try {
                if (output != null) output.close();
                if (input != null) input.close();
            } catch (IOException ignored) {}

            if (connection != null) connection.disconnect();
        }
    }).start();
}

下载回调

为了处理下载过程中的进度、成功与失败,我们需要定义一个回调接口:

public interface DownloadCallback {
    void onProgress(int progress);  // 下载进度
    void onSuccess(String filePath); // 下载完成
    void onFailed(String error);     // 下载失败
}

3.解压 ZIP 文件’

文件解压

下载完成后,我们可以解压 ZIP 文件。Android 提供了 ZipInputStream 来处理解压工作。以下是解压代码实现:

public void unzip(String zipFilePath, String targetDirectory, UnzipCallback callback) {
    new Thread(() -> {
        try {
            File destDir = new File(targetDirectory);
            if (!destDir.exists()) {
                destDir.mkdirs();
            }

            ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
            ZipEntry zipEntry;

            byte[] buffer = new byte[1024];
            while ((zipEntry = zis.getNextEntry()) != null) {
                File newFile = new File(destDir, zipEntry.getName());

                if (zipEntry.isDirectory()) {
                    newFile.mkdirs();
                } else {
                    // 确保父目录存在
                    new File(newFile.getParent()).mkdirs();

                    FileOutputStream fos = new FileOutputStream(newFile);
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                    fos.close();
                }
                zis.closeEntry();
            }

            zis.close();
            callback.onSuccess(targetDirectory);

        } catch (IOException e) {
            e.printStackTrace();
            callback.onFailed(e.getMessage());
        }
    }).start();
}

解压回调

为了处理解压过程中的状态,我们也需要一个回调接口:

public interface UnzipCallback {
    void onSuccess(String targetPath);  // 解压成功
    void onFailed(String error);        // 解压失败
}

4.断点续传

对于较大的文件下载,可能需要实现断点续传功能。为了实现这一点,我们可以在下载时存储已下载的字节数,并在中断后继续下载。

修改 downloadZipFile 方法,使用 Range 头来支持断点续传:

public void downloadZipFileWithResume(String urlStr, String savePath, DownloadCallback callback) {
    new Thread(() -> {
        InputStream input = null;
        FileOutputStream output = null;
        HttpURLConnection connection = null;

        try {
            File file = new File(savePath);
            long downloadedLength = file.exists() ? file.length() : 0;
            URL url = new URL(urlStr);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(10000);
            connection.setRequestProperty("Range", "bytes=" + downloadedLength + "-");

            connection.connect();

            if (connection.getResponseCode() != HttpURLConnection.HTTP_PARTIAL &&
                connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                callback.onFailed("Server returned HTTP " + connection.getResponseCode());
                return;
            }

            int fileLength = connection.getContentLength() + (int) downloadedLength;

            input = connection.getInputStream();
            output = new FileOutputStream(file, true);  // 以追加方式写入

            byte[] buffer = new byte[4096];
            int bytesRead;
            long total = downloadedLength;

            while ((bytesRead = input.read(buffer)) != -1) {
                total += bytesRead;
                output.write(buffer, 0, bytesRead);
                int progress = (int) (total * 100 / fileLength);
                callback.onProgress(progress);
            }

            callback.onSuccess(file.getAbsolutePath());

        } catch (Exception e) {
            e.printStackTrace();
            callback.onFailed(e.getMessage());
        } finally {
            try {
                if (output != null) output.close();
                if (input != null) input.close();
            } catch (IOException ignored) {}

            if (connection != null) connection.disconnect();
        }
    }).start();
}

5.调用示例

以下是如何使用我们实现的功能来下载、解压并处理回调:

// 下载地址和存储路径
String zipUrl = "https://example.com/path/to/file.zip";
File downloadDir = context.getExternalFilesDir(null); // 应用私有目录
String zipFilePath = new File(downloadDir, "downloaded_file.zip").getAbsolutePath();
String unzipTargetDir = new File(downloadDir, "unzipped_folder").getAbsolutePath();

// 下载 ZIP 文件
downloadZipFile(zipUrl, zipFilePath, new DownloadCallback() {
    @Override
    public void onProgress(int progress) {
        Log.d("Download", "Progress: " + progress + "%");
    }

    @Override
    public void onSuccess(String filePath) {
        Log.d("Download", "Download completed: " + filePath);

        // 解压 ZIP 文件
        unzip(filePath, unzipTargetDir, new UnzipCallback() {
            @Override
            public void onSuccess(String targetPath) {
                Log.d("Unzip", "Unzip completed at: " + targetPath);
            }

            @Override
            public void onFailed(String error) {
                Log.e("Unzip", "Unzip failed: " + error);
            }
        });
    }

    @Override
    public void onFailed(String error) {
        Log.e("Download", "Download failed: " + error);
    }
});

6.常见问题与优化建议

在实际开发中,下载和解压 ZIP 文件的过程中会遇到一些常见的问题。以下是一些优化建议和处理方法:

网络连接中断

在进行大文件下载时,网络连接可能会中断,导致下载失败。为了避免重复下载,建议使用断点续传技术。断点续传技术通过记录文件下载的进度,从而在网络中断后可以从中断位置继续下载,而不是重新开始。

优化建议:

  • 使用 Range 请求头来实现文件下载的断点续传(如上文所示)。
  • 在网络中断时,将已经下载的字节数保存在本地文件中,以便恢复下载。

文件解压失败

在解压 ZIP 文件时,如果文件结构复杂或者出现损坏,可能会导致解压失败。确保文件完整性是防止解压失败的关键。

优化建议:

  • 在解压前,可以验证 ZIP 文件的完整性,确保下载完成且未损坏。
  • 使用 ZipFile 类可以简化一些 ZIP 文件的解压操作,并能更好地处理压缩包中的特殊情况(如加密压缩包)。

性能优化

  • 缓冲区大小:在下载和解压文件时,使用适当大小的缓冲区(例如 4096 字节)可以提升性能。
  • UI 线程阻塞:确保所有的网络和解压操作都在后台线程中执行,避免阻塞 UI 线程,导致应用无响应。

文件存储权限问题

在 Android 10 及以上版本,Google 对外部存储的访问权限做出了更严格的限制。你需要使用 getExternalFilesDir() 方法来存储文件,这个目录是私有的,仅限应用本身访问,且不会在卸载应用时删除。

优化建议:

  • 使用 getExternalFilesDir() 或 MediaStore 来确保兼容性。
  • 针对 Android 11 及以上版本,需申请 MANAGE_EXTERNAL_STORAGE 权限以便访问共享存储。

文件大小限制

大文件的下载和解压会占用大量存储空间,尤其是当设备存储较满时,可能会导致下载失败或存储不足的问题。

优化建议:

  • 在下载前,检查设备存储空间是否足够,并提示用户进行清理。
  • 如果需要处理大文件,考虑在下载过程中显示文件大小,并在下载完成前通过 onProgress 更新下载进度。

http://www.kler.cn/a/592448.html

相关文章:

  • 知识图谱知识库汇总
  • 算法刷题区域部分反转
  • Git 新建本地分支并关联到远程仓库
  • RG-S3760应用协议配置
  • 基于javaweb的SpringBoot药房管理系统设计与实现(源码+文档+部署讲解)
  • 逆向入门 汇编语言IDA的基本使用
  • 【java面型对象进阶】------继承实例
  • VBA技术资料MF281:驱动器列表和类型
  • 学习threejs,构建THREE.ParametricGeometry参数化函数生成几何体
  • 基于 Quest 摄像头数据开发的原理介绍【Unity Meta Quest MR 开发教程】
  • 微前端 qiankun vite vue3
  • 使用码云搭建CocoaPods远程私有库
  • 人事档案管理系统基于Spring BootSSM
  • LS-NET-006-思科MDS 9148S 查看内存
  • 【微服务】基于Lambda ESM的预留模式调整Kafka ESM吞吐量的实战
  • Spring Boot集成MyBatis与MySQL
  • Swagger-告别手写文档
  • 第十五届蓝桥杯C/C++组:宝石组合题目(从小学奥数到编程题详解)
  • 【嵌入式Linux】基于ArmLinux的智能垃圾分类系统项目
  • 构建高效复杂系统的关键:架构与模块详解