草庐IT

Android App开发网络通信中使用okhttp下载和上传图片、文件讲解及实战(超详细实现用户注册信息上传 附源码)

showswoller 2023-10-02 原文

需要源码请点赞关注收藏后评论区留言并且私信~~~

一、使用okhttp下载图片

okhttp不但简化了HTTP接口的调用过程,连下载文件都变得简单了,对于一般的文件下载,按照常规的GET方式调用流程,只要重写回调方法onResponse,在该方法中通过应答对象的body方法即可获得应答的数据包对象,调用数据包对象的string方法即可得到文本形式的字符串,下面以下载网络图片为例,位图工具BitmapFactory刚好提供了decodeStream方法,允许直接从输入流中解码获取位图对象 效果如下

点击下载图片按钮后即可自动实现下载网络图片

二、利用okhttp下载文件

当然,网络文件不止是图片,还有各种各样的文件,这些文件没有专门的解码工具,只能从输入流老老实实的读取字节数据,不过读取字节数据有个好处,就是能够根据已经读写的数据长度计算下载进度,特别在下载大文件的时候,实时展示当前的下载进度非常有用

效果如下 演示视频已上传至个人主页 可自行观看

由下图可见下载进度加载到了百分之一百

  

 代码如下

Java类

package com.example.network;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import 

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;


 = "https://ptgl.fujian.gov.cn:8088/masvod/public/2021/03/19/20210319_178498bcae9_r38.mp4";
    private TextView tv_result; // 声明一个文本视图对象
    private TextView tv_progress; // 声明一个文本视图对象
    private ImageView iv_result; // 声明一个图像视图对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_okhttp_download);
        tv_result = findViewById(R.id.tv_result);
        tv_progress = findViewById(R.id.tv_progress);
        iv_result = findViewById(R.id.iv_result);
        findViewById(R.id.btn_download_image).setOnClickListener(v -> downloadImage());
        findViewById(R.id.btn_download_file).setOnClickListener(v -> downloadFile());
    }

    // 下载网络图片
    private void downloadImage() {
        tv_progress.setVisibility(View.GONE);
        iv_result.setVisibility(View.VISIBLE);
        OkHttpClient client = new OkHttpClient(); // 创建一个okhttp客户端对象
        // 创建一个GET方式的请求结构
        Request request = new Request.Builder().url(URL_IMAGE).build();
        Call call = client.newCall(request); // 根据请求结构创建调用对象
        // 加入HTTP请求队列。异步调用,并设置接口应答的回调方法
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) { // 请求失败
                // 回到主线程操纵界面
                runOnUiThread(() -> tv_result.setText("下载网络图片报错:"+e.getMessage()));
            }

            @Override
            public void onResponse(Call call, final Response response) { // 请求成功
                InputStream is = response.body().byteStream();
                // 从返回的输入流中解码获得位图数据
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                String mediaType = response.body().contentType().toString();
                long length = response.body().contentLength();
                String desc = String.format("文件类型为%s,文件大小为%d", mediaType, length);
                // 回到主线程操纵界面
                runOnUiThread(() -> {
                    tv_result.setText("下载网络图片返回:"+desc);
                    iv_result.setImageBitmap(bitmap);
                });
            }
        });
    }

    // 下载网络文件
    private void downloadFile() {
        tv_progress.setVisibility(View.VISIBLE);
        iv_result.setVisibility(View.GONE);
        OkHttpClient client = new OkHttpClient(); // 创建一个okhttp客户端对象
        // 创建一个GET方式的请求结构
        Request request = new Request.Builder().url(URL_APK).build();
        Call call = client.newCall(request); // 根据请求结构创建调用对象
        // 加入HTTP请求队列。异步调用,并设置接口应答的回调方法
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) { // 请求失败
                // 回到主线程操纵界面
                runOnUiThread(() -> tv_result.setText("下载网络文件报错:"+e.getMessage()));
            }

            @Override
            public void onResponse(Call call, final Response response) { // 请求成功
                String mediaType = response.body().contentType().toString();
                long length = response.body().contentLength();
                String desc = String.format("文件类型为%s,文件大小为%d", mediaType, length);
                // 回到主线程操纵界面
                runOnUiThread(() -> tv_result.setText("下载网络文件返回:"+desc));
                String path = String.format("%s/%s.apk",
                        getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),
                        DateUtil.getNowDateTime());
                // 下面从返回的输入流中读取字节数据并保存为本地文件
                try (InputStream is = response.body().byteStream();
                     FileOutputStream fos = new FileOutputStream(path)) {
                    byte[] buf = new byte[100 * 1024];
                    int sum=0, len=0;
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                        sum += len;
                        int progress = (int) (sum * 1.0f / length * 100);
                        String detail = String.format("文件保存在%s。已下载%d%%", path, progress);
                        // 回到主线程操纵界面
                        runOnUiThread(() -> tv_progress.setText(detail));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/btn_download_image"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="下载图片"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Button
            android:id="@+id/btn_download_file"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="下载文件"
            android:textColor="@color/black"
            android:textSize="17sp" />
    </LinearLayout>

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <TextView
        android:id="@+id/tv_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <ImageView
        android:id="@+id/iv_result"
        android:layout_width="match_parent"
        android:layout_height="250dp" />

</LinearLayout>

三、利用okhttp上传文件

okhttp不仅让下载文件变得简单,还让上传文件变得更加灵活易用,比如修改个人资料,头像的时候常常带着文字说明,对于这种组合上传的业务场景,HttpURLConnection编码十分困难,用okhttp就十分简单,它引入分段结构MultipartBody及其建造器,分别适用于文本格式与文件格式的数据

下面举带头像进行用户注册的例子,既要把用户名和密码送给服务端,还要把头像图片传给服务器端

可以自定义用户信息

 

 代码如下

Java类

package com.example.network;

import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.example.network.constant.NetConst;
import com.example.network.util.BitmapUtil;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class OkhttpUploadActivity extends AppCompatActivity {
    private final static String TAG = "OkhttpUploadActivity";
    public final static String URL_REGISTER = NetConst.HTTP_PREFIX + "register";
    private EditText et_username; // 声明一个编辑框对象
    private EditText et_password; // 声明一个编辑框对象
    private TextView tv_result; // 声明一个文本视图对象
    private ImageView iv_face; // 声明一个图像视图对象
    private int CHOOSE_CODE = 3; // 只在相册挑选图片的请求码
    private List<String> mPathList = new ArrayList<>(); // 头像文件的路径列表

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_okhttp_upload);
        et_username = findViewById(R.id.et_username);
        et_password = findViewById(R.id.et_password);
        iv_face = findViewById(R.id.iv_face);
        tv_result = findViewById(R.id.tv_result);
        iv_face.setOnClickListener(v -> {
            // 创建一个内容获取动作的意图(准备跳到系统相册)
            Intent albumIntent = new Intent(Intent.ACTION_GET_CONTENT);
            albumIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); // 是否允许多选
            albumIntent.setType("image/*"); // 类型为图像
            startActivityForResult(albumIntent, CHOOSE_CODE); // 打开系统相册
        });
        findViewById(R.id.btn_register).setOnClickListener(v -> uploadFile());
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (resultCode == RESULT_OK && requestCode == CHOOSE_CODE) { // 从相册返回
            mPathList.clear();
            if (intent.getData() != null) { // 从相册选择一张照片
                // 把指定Uri的图片复制一份到内部存储空间,并返回存储路径
                String imagePath = saveImage(intent.getData());
                mPathList.add(imagePath);
            }
        }
    }

    // 把指定Uri的图片复制一份到内部存储空间,并返回存储路径
    private String saveImage(Uri uri) {
        String uriStr = uri.toString();
        String imageName = uriStr.substring(uriStr.lastIndexOf("/")+1);
        String imagePath = String.format("%s/%s.jpg", 
                getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(), imageName);
        // 获得自动缩小后的位图对象
        Bitmap bitmap = BitmapUtil.getAutoZoomImage(this, uri);
        // 把位图数据保存到指定路径的图片文件
        BitmapUtil.saveImage(imagePath, bitmap);
        iv_face.setImageBitmap(bitmap);
        return imagePath;
    }

    // 执行文件上传动作
    private void uploadFile() {
        if (mPathList.size() <= 0) {
            Toast.makeText(this, "请选择待上传的用户头像", Toast.LENGTH_SHORT).show();
            return;
        }
        // 创建分段内容的建造器对象
        MultipartBody.Builder builder = new MultipartBody.Builder();
        String username = et_username.getText().toString();
        String password = et_password.getText().toString();
        if (!TextUtils.isEmpty(username)) {
            // 往建造器对象添加().string();
                // 回到主线程操纵界面
                runOnUiThread(() -> tv_result.setText("调用注册接口返回:\n"+resp));
            }
        });
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="用户名:"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <EditText
            android:id="@+id/et_username"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/editext_selector"
            android:gravity="left|center"
            android:hint="请输入用户名"
            android:maxLength="11"
            android:textColor="@color/black"
            android:textSize="17sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginTop="10dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="密 码:"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <EditText
            android:id="@+id/et_password"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/editext_selector"
            android:gravity="left|center"
            android:hint="请输入密码"
            android:inputType="numberPassword"
            android:maxLength="6"
            android:textColor="@color/black"
            android:textSize="17sp" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="头 像:"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <ImageView
            android:id="@+id/iv_face"
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:layout_marginLeft="5dp"
            android:scaleType="fitXY"
            android:src="@drawable/add_pic" />
    </LinearLayout>

    <Button
        android:id="@+id/btn_register"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="注册"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/black"
        android:textSize="17sp" />

</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

有关Android App开发网络通信中使用okhttp下载和上传图片、文件讲解及实战(超详细实现用户注册信息上传 附源码)的更多相关文章

  1. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  2. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

  3. ruby-on-rails - 有没有办法为 CarrierWave/Fog 设置上传进度指示器? - 2

    我在Rails应用程序中使用CarrierWave/Fog将视频上传到AmazonS3。有没有办法判断上传的进度,让我可以显示上传进度如何? 最佳答案 CarrierWave和Fog本身没有这种功能;你需要一个前端uploader来显示进度。当我不得不解决这个问题时,我使用了jQueryfileupload因为我的堆栈中已经有jQuery。甚至还有apostonCarrierWaveintegration因此您只需按照那里的说明操作即可获得适用于您的应用的进度条。 关于ruby-on-r

  4. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  5. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  6. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  7. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  8. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  9. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  10. ruby-on-rails - 安全地显示使用回形针 gem 上传的图像 - 2

    默认情况下:回形针gem将所有附件存储在公共(public)目录中。出于安全原因,我不想将附件存储在公共(public)目录中,所以我将它们保存在应用程序根目录的uploads目录中:classPost我没有指定url选项,因为我不希望每个图像附件都有一个url。如果指定了url:那么拥有该url的任何人都可以访问该图像。这是不安全的。在user#show页面中:我想实际显示图像。如果我使用所有回形针默认设置,那么我可以这样做,因为图像将在公共(public)目录中并且图像将具有一个url:Someimage:看来,如果我将图像附件保存在公共(public)目录之外并且不指定url(同

随机推荐