实现Android文件下载和上传功能的详细指南

在移动应用开发中,文件下载和上传功能是常见需求之一。无论是下载应用内的资源文件还是上传用户生成的内容,这两项功能都是必不可少的。在本篇博客中,我们将详细介绍如何在Android应用中实现文件下载和上传功能,以及如何在过程中实现进度跟踪。

引言

在许多应用中,我们经常需要从网络上下载文件,例如图片、音频、视频或其他资源文件。同样,应用也可能需要允许用户上传文件,比如照片、文档等。为了实现这些功能,我们需要考虑到以下几个方面:

权限管理:确保应用有足够的权限来执行文件操作。
下载功能:从网络获取文件并保存到设备中。
上传功能:将设备上的文件上传到服务器。
进度跟踪:在下载和上传过程中,实时更新进度以提供用户反馈。
在本文中,我们将使用Java语言和Android开发环境来实现文件下载和上传功能,并且在这个过程中展示如何实现进度跟踪。

引入权限

  <uses-permission android:name="android.permission.INTERNET" /> <!-- 允许应用程序访问互联网,用于进行网络通信。-->
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 允许应用程序写入外部存储,如 SD 卡。-->
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 允许应用程序读取外部存储的文件。-->

实现文件下载功能

我们首先来看如何实现文件下载功能。在Android中,我们可以使用OkHttp库来处理网络请求,并使用相关的权限来保存文件到设备中。

// 在 FileDownloader 类中实现 downloadFile 方法
public static void downloadFile(Activity activity, Context context, String fileUrl, String fileName, String dvrOTA, DownloadCallback callback) {
    // 检查应用是否具有写入外部存储的权限
    if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE_PERMISSION);
        return;
    }

    // 获取文件保存路径
    String externalStorageDir = FileUtil.filePathDir();
    List<String> usbPath = USBChecker.getUsbPath();
    if(usbPath!=null && usbPath.size() > 0){
        externalStorageDir = usbPath.get(0);
    }

    // 如果外部存储路径不可用,则回调失败信息
    if (externalStorageDir == null) {
        callback.onFailure("External_storage_not_available");
        return;
    }

    // 构建文件保存目录
    String fileDir = externalStorageDir + File.separator + dvrOTA;
    File fileDirFile = new File(fileDir);
    if (!fileDirFile.exists()) {
        boolean created = fileDirFile.mkdirs();
        if (!created) {
            callback.onFailure("Failed to create file");
            return;
        }
    }

    // 构建文件保存路径
    String filePath = fileDir + File.separator + fileName;
    File targetFile = new File(filePath);
    if (!targetFile.exists()) {
        try {
            boolean created = targetFile.createNewFile();
            if (!created) {
                callback.onFailure("Failed to create file");
                return;
            }
        } catch (IOException e) {
            callback.onFailure("Failed to create file: " + e.getMessage());
            return;
        }
    }

    // 构建下载请求
    Request request = new Request.Builder().url(fileUrl).build();

    // 使用 OkHttp 发起异步请求
    NetworkUtils.getOkHttpClientInstance().newCall(request).enqueue(new Callback() {
        @Override
        public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
            if (response.isSuccessful() && response.body() != null) {
                long fileSize = Objects.requireNonNull(response.body()).contentLength();
                long fileSizeDownloaded = 0;

                // 读取响应流并写入文件
                try (InputStream inputStream = response.body().byteStream();
                     OutputStream outputStream = new FileOutputStream(targetFile)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                        fileSizeDownloaded += bytesRead;
                        int progress = (int) ((fileSizeDownloaded * 100) / fileSize);
                        callback.onProgress(progress);
                    }
                    callback.onSuccess(targetFile);
                } catch (IOException e) {
                    callback.onFailure(e.getMessage());
                }
            } else {
                callback.onFailure("Failed to download file");
            }
        }

        @Override
        public void onFailure(@NonNull Call call, @NonNull IOException e) {
            callback.onFailure("Failed to download file: " + e.getMessage());
        }
    });
}

public class USBChecker {

    public static List<String> getUsbPath() {
        // 获取外部存储的状态
        List<String> usbPath = new ArrayList<>();
        File strongDir = new File("/storage");
        File[] files = strongDir.listFiles();
        if(files!=null){
            for (File file: files) {
                if(file.isDirectory() && file.canRead() && isUsbDevice(file)){
                    usbPath.add(file.getAbsolutePath());
                }
            }
        }
        return usbPath;
    }

    public static boolean isUsbDevice(File directory){
        if(directory.getName().contains("sd") || directory.getName().contains("udisk")){
            return true;
        }
        return false;
    }

}
    public interface DownloadCallback {
        void onProgress(int progress);

        void onSuccess(File file);

        void onFailure(String errorMessage);
    }
public class FileUtil {
	public static String filePathDir(){
        if(Build.VERSION.SDK_INT > 28){
            return MyApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath();
        }else {
            return Environment.getExternalStorageDirectory().getPath();
        }
    }
}

在这段代码中,我们首先检查应用是否具有写入外部存储的权限。如果没有权限,我们会向用户请求权限。接下来,我们构建文件保存路径,并创建必要的目录。然后,我们构建下载请求并使用OkHttp库来执行异步请求。在响应中,我们读取响应流并将其写入文件中,同时更新下载进度。最后,我们通过回调函数通知下载结果。

实现文件上传功能

接下来,让我们来看如何实现文件上传功能。在这里,我们将使用OkHttp库来执行文件上传,并在上传过程中实现进度跟踪。

    public static void uploadFile(String fileName, DownloadCallback callback) {

        File file = new File(fileName);
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(),
                        RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();

        // 使用自定义的进度RequestBody
        ProgressRequestBody progressRequestBody = new ProgressRequestBody(requestBody, new ProgressListener() {
            @Override
            public void onProgressChanged(long bytesWritten, long totalBytes) {
                // 计算并通知进度
                int progress = (int) (100 * bytesWritten / totalBytes);
                if(progress < 100){
                    callback.onProgress(progress);
                }
                if(progress == 100){
                    callback.onSuccess(file);
                }
            }
        });

        Request request = new Request.Builder()
                .url("http://"+ Config.DVR_IP +"/action/upload")
                .post(progressRequestBody)
                .build();

        NetworkUtils.getOkHttpClientInstance().newCall(request).enqueue(new Callback() {
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                if (response.isSuccessful() && response.body() != null) {
                    callback.onSuccess(file);
                } else {
                    callback.onFailure("Failed to upload file");
                }
            }

            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                callback.onFailure("Failed to upload file: " + e.getMessage());
            }
        });
    }

    // 定义上传进度监听器接口
    interface ProgressListener {
        void onProgressChanged(long bytesWritten, long totalBytes);
    }

    // 自定义RequestBody来包装实际的RequestBody以监听上传进度
    static class ProgressRequestBody extends RequestBody {
        private final RequestBody requestBody;
        private final ProgressListener progressListener;

        ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {
            this.requestBody = requestBody;
            this.progressListener = progressListener;
        }

        @Override
        public MediaType contentType() {
            return requestBody.contentType();
        }

        @Override
        public long contentLength() throws IOException {
            return requestBody.contentLength();
        }

        @Override
        public void writeTo(BufferedSink sink) throws IOException {
            BufferedSink bufferedSink = Okio.buffer(new ForwardingSink(sink) {
                long bytesWritten = 0L;
                long totalBytes = 0L;

                @Override
                public void write(Buffer source, long byteCount) throws IOException {
                    super.write(source, byteCount);
                    if (totalBytes == 0) {
                        totalBytes = contentLength();
                    }
                    bytesWritten += byteCount;
                    progressListener.onProgressChanged(bytesWritten, totalBytes);
                }
            });
            requestBody.writeTo(bufferedSink);
            bufferedSink.flush();
        }
    }

在这段代码中,我们首先构建上传文件的请求体,并使用自定义的进度RequestBody来包装实际的RequestBody以监听上传进度。然后,我们构建上传请求并使用OkHttp库来执行异步上传请求。在响应中,我们通过回调函数通知上传结果。

实现文件下载及上传功能工具类

FileDownloader

public class FileDownloader {

    public interface DownloadCallback {
        void onProgress(int progress);

        void onSuccess(File file);

        void onFailure(String errorMessage);
    }

    private static final int REQUEST_CODE_STORAGE_PERMISSION = 1;

    public static void downloadFile(Activity activity, Context context, String fileUrl, String fileName, String dvrOTA, DownloadCallback callback) {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_CODE_STORAGE_PERMISSION);
            return;
        }

        String externalStorageDir = FileUtil.filePathDir();
        List<String> usbPath = USBChecker.getUsbPath();
        if(usbPath!=null && usbPath.size() > 0){
            externalStorageDir = usbPath.get(0);
        }

        if (externalStorageDir == null) {
            callback.onFailure("External_storage_not_available");
            return;
        }

        String fileDir = externalStorageDir + File.separator + dvrOTA;
        File fileDirFile = new File(fileDir);
        if (!fileDirFile.exists()) {
            boolean created = fileDirFile.mkdirs();
            if (!created) {
                callback.onFailure("Failed to create file");
                return;
            }
        }

        String filePath = fileDir + File.separator + fileName;
        File targetFile = new File(filePath);
        if (!targetFile.exists()) {
            try {
                boolean created = targetFile.createNewFile();
                if (!created) {
                    callback.onFailure("Failed to create file");
                    return;
                }
            } catch (IOException e) {
                callback.onFailure("Failed to create file: " + e.getMessage());
                return;
            }
        }

        Request request = new Request.Builder().url(fileUrl).build();

        NetworkUtils.getOkHttpClientInstance().newCall(request).enqueue(new Callback() {
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                if (response.isSuccessful() && response.body() != null) {
                    long fileSize = Objects.requireNonNull(response.body()).contentLength();
                    long fileSizeDownloaded = 0;

                    try (InputStream inputStream = response.body().byteStream();
                         OutputStream outputStream = new FileOutputStream(targetFile)) {
                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                            fileSizeDownloaded += bytesRead;
                            int progress = (int) ((fileSizeDownloaded * 100) / fileSize);
                            Log.d("Download", "Progress: " + progress);
                            callback.onProgress(progress);
                        }
                        callback.onSuccess(targetFile);
                    } catch (IOException e) {
                        callback.onFailure(e.getMessage());
                    }
                } else {
                    callback.onFailure("Failed to download file");
                }
            }

            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                callback.onFailure("Failed to download file: " + e.getMessage());
            }
        });
    }

    public static void uploadFile(String fileName, DownloadCallback callback) {

        File file = new File(fileName);
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(),
                        RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();

        // 使用自定义的进度RequestBody
        ProgressRequestBody progressRequestBody = new ProgressRequestBody(requestBody, new ProgressListener() {
            @Override
            public void onProgressChanged(long bytesWritten, long totalBytes) {
                // 计算并通知进度
                int progress = (int) (100 * bytesWritten / totalBytes);
                if(progress < 100){
                    callback.onProgress(progress);
                }
                if(progress == 100){
                    callback.onSuccess(file);
                }
            }
        });

        Request request = new Request.Builder()
                .url("http://"+ Config.DVR_IP +"/action/upload")
                .post(progressRequestBody)
                .build();

        NetworkUtils.getOkHttpClientInstance().newCall(request).enqueue(new Callback() {
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                if (response.isSuccessful() && response.body() != null) {
                    callback.onSuccess(file);
                } else {
                    callback.onFailure("Failed to upload file");
                }
            }

            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                callback.onFailure("Failed to upload file: " + e.getMessage());
            }
        });
    }

    // 定义上传进度监听器接口
    interface ProgressListener {
        void onProgressChanged(long bytesWritten, long totalBytes);
    }

    // 自定义RequestBody来包装实际的RequestBody以监听上传进度
    static class ProgressRequestBody extends RequestBody {
        private final RequestBody requestBody;
        private final ProgressListener progressListener;

        ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {
            this.requestBody = requestBody;
            this.progressListener = progressListener;
        }

        @Override
        public MediaType contentType() {
            return requestBody.contentType();
        }

        @Override
        public long contentLength() throws IOException {
            return requestBody.contentLength();
        }

        @Override
        public void writeTo(BufferedSink sink) throws IOException {
            BufferedSink bufferedSink = Okio.buffer(new ForwardingSink(sink) {
                long bytesWritten = 0L;
                long totalBytes = 0L;

                @Override
                public void write(Buffer source, long byteCount) throws IOException {
                    super.write(source, byteCount);
                    if (totalBytes == 0) {
                        totalBytes = contentLength();
                    }
                    bytesWritten += byteCount;
                    progressListener.onProgressChanged(bytesWritten, totalBytes);
                }
            });
            requestBody.writeTo(bufferedSink);
            bufferedSink.flush();
        }
    }

}

FileUtil

    public static String filePathDir(){
        if(Build.VERSION.SDK_INT > 28){
            return MyApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath();
        }else {
            return Environment.getExternalStorageDirectory().getPath();
        }
    }

USBChecker

public class USBChecker {

    public static List<String> getUsbPath() {
        // 获取外部存储的状态
        List<String> usbPath = new ArrayList<>();
        File strongDir = new File("/storage");
        File[] files = strongDir.listFiles();
        if(files!=null){
            for (File file: files) {
                if(file.isDirectory() && file.canRead() && isUsbDevice(file)){
                    usbPath.add(file.getAbsolutePath());
                }
            }
        }
        return usbPath;
    }

    public static boolean isUsbDevice(File directory){
        if(directory.getName().contains("sd") || directory.getName().contains("udisk")){
            return true;
        }
        return false;
    }

}

调用文件下载及上传功能的方法

文件下载

    private void downFile() {
        FileDownloader.downloadFile(this, getApplicationContext(), fileUrl, fileName, filePath, new FileDownloader.DownloadCallback() {
            @Override
            public void onProgress(int progress) {
                // 更新下载进度
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        progress;
                    }
                });
            }

            @Override
            public void onSuccess(File file) {
                // 下载成功,处理文件
                Log.e("progress---", "onSuccess");
            }

            @Override
            public void onFailure(String errorMessage) {
                // 下载失败,处理错误
                Log.e("progress---", "" + errorMessage);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (errorMessage.equals("External_storage_not_available")) {
                            ToastHelps.showShortTime("下载失败,请检查存储卡");
                        } else {
                            ToastHelps.showShortTime("下载失败");
                        }
                    }
                });
            }
        });
    }

文件上传

    private void uploadFile() {
        FileDownloader.uploadFile(absolutePath, new FileDownloader.DownloadCallback() {
            @Override
            public void onProgress(int progress) {
                // 更新下载进度
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        progress;
                    }
                });
            }

            @Override
            public void onSuccess(File file) {
                // 下载成功,处理文件
                Log.e("progress---", "onSuccess");
            }

            @Override
            public void onFailure(String errorMessage) {
                // 下载失败,处理错误
                Log.e("progress---", "" + errorMessage);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (errorMessage.equals("External_storage_not_available")) {
                            ToastHelps.showShortTime("升级失败,请检查存储卡");
                        } else {
                            ToastHelps.showShortTime("升级失败");
                        }
                    }
                });
            }
        });
    }

总结

在本文中,我们介绍了如何在Android应用中实现文件下载和上传功能,并实现了进度跟踪功能。通过使用OkHttp库处理网络请求和自定义的进度RequestBody,我们能够轻松地实现这些功能。希望本文能够帮助开发者更好地理解文件操作和网络请求,并能够在实际应用中应用这些技术。

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
垃圾分类助手app的设计与实现是使用Java语言,并在Android平台上开发的。开发工具主要是Android开发工具包(Android Development Tools,ADT)和Android Studio。 在开发环境方面,首先需要安装Java开发套件(JDK),以便在计算机上使用Java编程语言进行开发。然后,需要下载和安装Android SDK,它包含了用于开发Android应用程序的各种工具和库。 接下来,开始使用Android开发工具包(ADT)或最新版本的Android Studio创建一个新的Android项目。这个项目将包括应用程序的基础结构和源代码,并提供了一个用于设计和实现用户界面的可视化编辑器。 在应用程序设计方面,首先需要确定垃圾分类助手app的功能和界面设计。这可能包括显示垃圾分类规则和指南,提供垃圾分类相关的信息和解答常见问题,以及提供用户可上传自己所在地区的垃圾分类信息的功能。 在实现过程中,需要根据应用程序的设计创建相应的Java类和布局文件。Java类将处理应用程序的逻辑,并提供与用户界面的交互。布局文件将定义应用程序中的各个屏幕和视图的外观和排列方式。 开发过程中需要编写代码来实现垃圾分类助手app的各种功能,例如通过网络请求获取垃圾分类信息、解析和显示数据、处理用户输入操作等。 最后,进行测试和调试以确保垃圾分类助手app的功能和界面的正常运行。可以使用Android模拟器或连接到物理设备上进行测试和调试。 总结而言,垃圾分类助手app的设计与实现需要使用Java语言和Android开发工具来创建应用程序的基础结构、实现功能和设计用户界面,并经过测试和调试以确保正常运行。这个过程需要对Java和Android开发技术有一定的了解和熟悉。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android程序Su

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值