文章目录

  • GET请求
  • POST请求
  • POST上传各种类型的文件
  • POST提交字符串
  • POST提交json
  • POST提交byte数据
  • POST上传流
  • 获取流
  • 获取字节数组
  • 设置超时时间和缓存
  • 设置网络优先策略
  • https自签名证书验证
  • 忽略所有证书校验
  • 下载文件
  • 监听下载进度
  • 批量上传文件
  • 监听文件上传进度
  • 服务端代码实现
    • javabean
    • Action类
    • EncodingIntereptor
    • UploadUtils
    • struts.xml配置文件

首先引入okhttp框架

 compile 'com.squareup.okhttp3:okhttp:3.7.0'compile 'com.squareup.okio:okio:1.13.0'

另外,别忘了添加这些权限

<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"/>

GET请求

private void demo1() {try {//get请求的参数拼在url后,需要编码,同时服务器也需要解码String url ="http://192.168.30.217/shopping/test/demo1?username=" + URLEncoder.encode("胜哥", "utf-8") + "&age=22"//构建请求对象Request request = new Request.Builder().url(url) //请求的url设置//.method("GET", null)// method GET must not have a request body..get()//等于method("GET", null).header("Cookie", "session=123123") //添加请求头.build();//创建okhttpOkHttpClient client = new OkHttpClient();//创建callCall call = client.newCall(request);//异步执行请求call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println(e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println(response.body().string());//输出字符串结果}});} catch (UnsupportedEncodingException e) {e.printStackTrace();}
}

POST请求

private void demo2() {//application/x-www-form-urlencoded 普通表单提交,添加参数RequestBody body = new FormBody.Builder().add("username", "胜哥").add("age", "22").add("hobby", "吃").add("hobby", "喝").add("hobby", "玩").build();//构建请求对象        Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1").post(body) //post的body//.method("POST", body) 可以直接用post方法替代.header("Cookie", "session=123123") //添加请求头.build();//创建okhttpOkHttpClient client = new OkHttpClient();//创建callfinal Call call = client.newCall(request);//execute是同步的方法,必须在子线程中运行new Thread(new Runnable() {@Overridepublic void run() {try {Response response = call.execute(); //同步执行System.out.println(response.body().string()); //输出字符串结果} catch (IOException e) {e.printStackTrace();}}}).start();
}

POST上传各种类型的文件

private void demo3() {//File file = createFile("a.txt");//File file = createFile("timg.gif");//File file = createFile("b.jpg");File file = createFile("c.mp4");//构建文件bodyRequestBody body = RequestBody.create(MediaType.parse("text/x-markdown;charset=utf-8"), file);//或者//RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data; charset=utf-8"), file);//构建文件上传表单参数RequestBody multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM) //必须设置,multipart/form-data 才支持文件上传.addFormDataPart("upload", file.getName(), body) //name是表单提交的name.addFormDataPart("username", "胜哥").addFormDataPart("age", "22").addFormDataPart("hobby", "吃").addFormDataPart("hobby", "喝").addFormDataPart("hobby", "玩").build();//构建请求对象        Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1").post(multiBody)//设置请求体数据.build();//创建okhttpOkHttpClient client = new OkHttpClient();//创建callCall call = client.newCall(request);//异步执行请求call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});}//将asset目录下的文件转成file保存到手机存储,并返回
private File createFile(String fileName) {File file = null;if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {file = new File(Environment.getExternalStorageDirectory(), fileName);} else {file = new File(getCacheDir(), fileName);}if (!file.exists() || file.length() == 0) {try {BufferedInputStream bis = new BufferedInputStream(getAssets().open(fileName));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));int b;while ((b = bis.read()) != -1) {bos.write(b);}bos.flush();bos.close();bis.close();} catch (IOException e) {e.printStackTrace();}}return file;}

POST提交字符串

private void demo4() {RequestBody body = RequestBody.create(MediaType.parse("text/x-markdown;charset=utf-8"), "天上飞的是什么?");Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo2").post(body).build();OkHttpClient client = new OkHttpClient();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});
}

POST提交json

private void demo5() {String jsonStr = "{\"username\":\"胜哥\",\"age\":20}";RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonStr);Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo2").post(body).build();OkHttpClient client = new OkHttpClient();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});
}

POST提交byte数据

private void demo6() {byte[] bytes = "天上飞的是什么?".getBytes();RequestBody body = RequestBody.create(MediaType.parse("text/x-markdown; charset=utf-8"), bytes);Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo2").post(body).build();OkHttpClient client = new OkHttpClient();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});
}

POST上传流

private void demo7() {OkHttpClient client = new OkHttpClient();RequestBody body = new RequestBody() {@Overridepublic MediaType contentType() {return MediaType.parse("text/x-markdown; charset=utf-8");}@Overridepublic void writeTo(BufferedSink sink) throws IOException {sink.writeUtf8("天上飞的是什么?");}};Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo2").post(body).build();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});
}

获取流

Response可以通过body获取到流,在回调结果中才能拿到Response对象

@Override
public void onResponse(Call call, Response response) throws IOException {//获取流InputStream stream = response.body().byteStream();BufferedReader br = new BufferedReader(new InputStreamReader(stream));StringBuilder sb = new StringBuilder();String line = null;while ((line = br.readLine()) != null) {sb.append(line);}System.out.println(sb);
}

获取字节数组

Response也可以获取字节数组

@Override
public void onResponse(Call call, Response response) throws IOException {//获取字节数组byte[] bytes = response.body().bytes();System.out.println(new String(bytes, "utf-8"));
}

设置超时时间和缓存

private OkHttpClient getOkHttpClient() {int cacheSize = 10 * 1024 * 1024; //设置缓存大小File cacheFile; //缓存的路径if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {cacheFile = new File(getExternalCacheDir(), "okCache");} else {cacheFile = new File(getCacheDir(), "okCache");}return new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS) //连接超时.writeTimeout(20, TimeUnit.SECONDS) //写超时.readTimeout(20, TimeUnit.SECONDS)//读超时.cache(new Cache(cacheFile, cacheSize)).build();
}

然后,在Request.Builder中通过cacheControl来控制是否需要读缓存

//设置超时时间和缓存
private void demo9() {OkHttpClient client = getOkHttpClient();Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1")//.cacheControl(CacheControl.FORCE_CACHE) //强制请求缓存,不会请求网络,没有缓存则回调异常//.cacheControl(CacheControl.FORCE_NETWORK) //强制请求网络,如果断网则直接回调异常.build();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (null != response.networkResponse()) {System.out.println("==来自网络==");} else if (null != response.cacheResponse()) {System.out.println("==来自缓存==");}System.out.println(response.body().string());}});
}

注意: OkHttp的缓存会受接口响应头的cache-control控制,我这里整理了一张缓存策略的表

特别注意:

FORCE_CACHE的情况,如果响应头是没有设置cache-control或者为no-cache的,即使本地有缓存,也直接回调异常,这里所说的本地有缓存是指在另外2种请求方式下得到的缓存,这里分2种情况,响应头有设置cache-control时的缓存和响应头没有设置cache-control的缓存,如果是没有设置cache-control响应头时缓存的,那么FORCE_CACHE的方式是直接回调异常的.

设置网络优先策略

上面例子可以看出Okhttp只能设置强制网络或者强制缓存策略,如果我们设置先请求网络,网络请求失败再请求缓存,也就是网络优先策略该怎么弄呢?

这个就需要我们来实现了,也很简单,就是在FORCE_NETWORK失败的时候判断一下

private void demo10() {final OkHttpClient client = getOkHttpClient();final Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1").cacheControl(CacheControl.FORCE_NETWORK)//强制网络.build();client.newCall(request).enqueue(new Callback() {int count = 0; //异常回调次数@Overridepublic void onFailure(Call call, IOException e) {if (count == 0) {//网络请求失败,再请求缓存count++;Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1").cacheControl(CacheControl.FORCE_CACHE) //强制缓存.build();client.newCall(request).enqueue(this);} else {System.out.println("onFailure:" + e.getMessage());}}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (null != response.networkResponse()) {System.out.println("==来自网络==");} else if (null != response.cacheResponse()) {System.out.println("==来自缓存==");}System.out.println(response.body().string());}});
}

https自签名证书验证

首先得拿到服务器提供的证书,关于如何创建签名证书可以看这篇文章Tomcat服务器支持https请求设置

拿到服务器的证书后,先将其存放到android项目的asserts目录下

OkHttpClient可以通OkHttpClient.Builder来设置sslSocketFactory,这样就可以校验自签名的证书了,通过下面这个方法创建SSLSocketFactory

public SSLSocketFactory getSSLSocketFactory(InputStream... certificates) {try {CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");// Create a KeyStore containing our trusted CAsKeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(null);int index = 0;for (InputStream certificate : certificates) {String certificateAlias = Integer.toString(index++);keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));try {if (certificate != null)certificate.close();} catch (IOException e) {}}// 通过KeyStore去引导生成的TrustManagerTrustManagerFactory trustManagerFactory =TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(keyStore);SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null,trustManagerFactory.getTrustManagers(),new SecureRandom());return sslContext.getSocketFactory();} catch (Exception e) {e.printStackTrace();}return null;
}

另外,OkHttpClient.Builder的sslSocketFactory方法中的第二参数需要接受一个X509TrustManager对象,这个对象可用于验证客户端和服务器的证书的合法性,通常不做处理,直接new出来

X509TrustManager x509TrustManager = new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
};

另外,为了避免访问服务器的域名和证书上配置的域名不符合,导致错误,还需要配置一下hostnameVerifier方法,忽略域名的校验
2种方式, 第一种直接new 一个 HostnameVerifier, 像这样,不做处理

HostnameVerifier hostnameVerifier = new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}
}

然后,传给OkHttpClient.Builder的hostnameVerifier方法即可.

另一种方式直接传org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER给OkHttpClient.Builder的hostnameVerifier方法即可.2种方式任选其一.

接下来就是设置OkHttpClient的sslSocketFactory了

private void demo14() {try {Request request = new Request.Builder().url("https://www.baidu.com"/*"https://192.168.30.217/shopping/test/demo1"*/).get().build();//仅信任自签名证书,2种方式//如果访问的不是自签名的网站就会报这个异常:java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.SSLSocketFactory sslSocketFactory = getSSLSocketFactory(getAssets().open("tomcat.cer")/*new Buffer().writeUtf8(tomcat_cre).inputStream()*/);X509TrustManager x509TrustManager = new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}};OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, x509TrustManager).hostnameVerifier(/*new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {//或者传org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER表示允许所有证书的域名return true;}}*/org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER).build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});} catch (Exception e) {e.printStackTrace();}}

看上面的注释提到SSLSocketFactory 有2种方式输入证书对应的流,上面介绍的是直接读取asserts目录下的tomcat.cer证书,如果你不想将证书拷贝到asserts中,你可以通过下面的方式解决

在证书所在的目录下打开cmd命令,输入下面命令
keytool -printcert -rfc -file tomcat.cer

会生成一段字符串,如下所示:

"-----BEGIN CERTIFICATE-----\n" +
"MIIDUzCCAjugAwIBAgIEbr1RnTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJj\n" +
"bjELMAkGA1UECBMCZ2QxCzAJBgNVBAcTAmd6MQ8wDQYDVQQKEwZ0b21jYXQxDzAN\n" +
"BgNVBAsTBnRvbWNhdDEPMA0GA1UEAxMGdG9tY2F0MB4XDTE5MDYxNDA4MjYyN1oX\n" +
"DTI5MDQyMjA4MjYyN1owWjELMAkGA1UEBhMCY24xCzAJBgNVBAgTAmdkMQswCQYD\n" +
"VQQHEwJnejEPMA0GA1UEChMGdG9tY2F0MQ8wDQYDVQQLEwZ0b21jYXQxDzANBgNV\n" +
"BAMTBnRvbWNhdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJtXs81j\n" +
"mX11QCGo6npLjqc5DoYpG6QRGw2ArlD6O/izlSx+N6DcS86kPXOAH+RF2DGH8fKv\n" +
"sKgZNAE01ZTYSSiLiVn+6WkmKqObuU2KnSosFiAkcC/n08lPyGkqNtQ4JQqiJy5v\n" +
"249EgJiqPJVXBfT9Sfk1Uv9fs6UGZwNMxDiyULiTDKLW85r6i14D9wW4XZm4gTBI\n" +
"I9+Ib65aB5RroGKI1pOKyqvF5WkByWeBU+vhTmh/lUKmHD9QGoV5EUfXPK2YYT+n\n" +
"EMQZZvpRkbQsRQ881WsTQAPc1/Sw5kqjSB1sGutr1pY9WVkBj3LNjgv08gLb+FVN\n" +
"CpFEeZUAngn2TBMCAwEAAaMhMB8wHQYDVR0OBBYEFCHsaPBTC14SD5rd+0gobwq6\n" +
"8gl/MA0GCSqGSIb3DQEBCwUAA4IBAQAGcY/vl0b/AWTDHNDu/XRXYEoJUiIG8KM9\n" +
"/zeBc+24uftWiiwsVIs+9EFVDAbexngu/fzFLg+KmWgSf2hiLgKDrD3sa+87xtkP\n" +
"5agBjSjOGE1BaC2UlZFLT1AFi1nTEzv/cq+dreqrjkTziSw4AaU8mFrDkhDrxrw2\n" +
"SZH6gb4hqtAF2vCSx3KfpQcPn3ebxUIKOMri7tbCaqW4ennh9D+0hUypwv5CAdiw\n" +
"bSBLv5E+SPGL1viqTeK85eMGUVlQyTA8AxtyYuToDj+Ppr5S1KlDfIgvhBwOdR/j\n" +
"iTcqD0gTORbGohhTCdDiYWGuj7LEoxyS/7un1Cr/UBTHOEn+4Vmp\n" +
"-----END CERTIFICATE-----"

然后调用getSSLSocketFactory方法的时候,通过okio的Buffer对象来将这段字符串生成流对象,即这段代码
InputStream inputStream = new Buffer().writeUtf8(tomcat_cre).inputStream()

忽略所有证书校验

private void demo14() {try {Request request = new Request.Builder().url("https://www.baidu.com").get().build();X509TrustManager x509TrustManager = new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}};//信任所有证书SSLContext mCtx = SSLContext.getInstance("TLS");mCtx.init(null, new TrustManager[]{x509TrustManager},null);sslSocketFactory = mCtx.getSocketFactory();OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, x509TrustManager).hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER).build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});} catch (Exception e) {e.printStackTrace();}}

下载文件

private void demo15() {final String fileName = "c.mp4";Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/download?fileName=" + fileName).get().build();OkHttpClient client = new OkHttpClient.Builder().build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {BufferedInputStream bis = new BufferedInputStream(response.body().byteStream());BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(createDownloadFile(fileName)));int b;while ((b = bis.read()) != -1) {bos.write(b);}bos.close();System.out.println("onResponse:下载完成!");}});}/*** 创建下载File** @return*/
private File createDownloadFile(String fileName) {File downloadDir;if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {downloadDir = new File(getExternalCacheDir(), "download");} else {downloadDir = new File(getCacheDir(), "download");}if (!downloadDir.exists()) {downloadDir.mkdirs();}final File downloadFile = new File(downloadDir, fileName);if (downloadFile.exists()) {downloadFile.delete();}return downloadFile;
}

监听下载进度

通过装饰者模式,对ResponseBody对象进行增强

public class ProgressResponseBody extends ResponseBody {public abstract static class Progress {public long startIndex() {//初始响应的位置return 0;}public boolean returnOnMainThread() {//在主线程中返回return true;}/*** 持续进度显示** @param bytesRead     当前已响应大小* @param contentLength 总文件大小* @param progress      响应进度0~100*/public abstract void onProgress(long bytesRead, long contentLength, int progress);}private Progress mProgress;private ResponseBody mResponseBody;private Handler mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {if (msg.what == 200) {long[] obj = (long[]) msg.obj;mProgress.onProgress(obj[0], obj[1], (int) obj[2]);}return true;}});public ProgressResponseBody(ResponseBody responseBody, Progress progress) {this.mProgress = progress;this.mResponseBody = responseBody;}@Overridepublic MediaType contentType() {return mResponseBody.contentType();}@Overridepublic long contentLength() {return mResponseBody.contentLength();}@Overridepublic BufferedSource source() {return Okio.buffer(new ForwardingSource(mResponseBody.source()) {//总读取字节数long totalBytesRead = 0L;@Overridepublic long read(Buffer sink, long byteCount) throws IOException {//当前已读字节数,若已读完则返回-1long bytesRead = super.read(sink, byteCount);if (mProgress != null) {totalBytesRead += bytesRead == -1 ? 0 : bytesRead;long curr = totalBytesRead + mProgress.startIndex();//如果有开始位置,则加上开始位置int progress = (int) (curr * 100 / contentLength());if (mProgress.returnOnMainThread()) {//将结果发送到UI线程中long[] obj = new long[]{curr, contentLength(), progress};mHandler.sendMessage(mHandler.obtainMessage(200, obj));} else {//结果保持在子线程中回调mProgress.onProgress(curr, contentLength(), progress);}}return bytesRead;}});}
}

然后通过OkHttpClient.Builder的addNetworkInterceptor方法设置拦截器,处理响应结果

private void demo17() {final String fileName = "c.mp4";Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/download?fileName=" + fileName).get().build();OkHttpClient client = new OkHttpClient.Builder().addNetworkInterceptor(new Interceptor() {@Overridepublic Response intercept(Chain chain) throws IOException {//获取原始ResponseResponse originalResponse = chain.proceed(chain.request());//返回新的Responsereturn originalResponse.newBuilder().body(new ProgressResponseBody(originalResponse.body(), new ProgressResponseBody.Progress() {@Overridepublic long startIndex() {return 0;}@Overridepublic void onProgress(long bytesRead, long contentLength, int progress) {System.out.println("bytesRead:" + bytesRead + " contentLength:" + contentLength +" progress:" + progress);}})).build();}}).build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("code:" + response.code());System.out.println("contentLength:" + response.header("Content-Length"));BufferedInputStream bis = new BufferedInputStream(response.body().byteStream());BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(createDownloadFile(fileName)));int b;while ((b = bis.read()) != -1) {bos.write(b);}bos.close();System.out.println("onResponse:下载完成!");}});
}

批量上传文件

private void demo18() {File file1 = createFile("c.mp4");File file2 = createFile("b.jpg");RequestBody file1Body = RequestBody.create(MediaType.parse("text/x-markdown;charset=utf-8"), file1);RequestBody file2Body = RequestBody.create(MediaType.parse("text/x-markdown;charset=utf-8"), file2);MultipartBody multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM) //必须设置,multipart/form-data 才支持文件上传.addFormDataPart("mulitupload", file1.getName(), file1Body) //name是表单提交的name.addFormDataPart("mulitupload", file2.getName(), file2Body) //name是表单提交的name.addFormDataPart("username", "胜哥").addFormDataPart("age", "22").addFormDataPart("hobby", "吃").addFormDataPart("hobby", "喝").addFormDataPart("hobby", "玩").build();Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1").post(multiBody).build();OkHttpClient client = new OkHttpClient();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});}

监听文件上传进度

通过装饰者模式对RequestBody进行增强,这里我们只需要监听文件的上传进度即可,普通参数的提交监听进度意义不大,基本很快就提交完了

/*** Created by mChenys on 2019/6/17.*/public class ProgressRequestBody extends RequestBody {private RequestBody mRequestBody;private Progress mProgress;private long totalByte;private Handler mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {if (msg.what == 200) {long[] obj = (long[]) msg.obj;mProgress.onProgress(obj[0], obj[1], (int) obj[2]);}return true;}});public abstract static class Progress {public long startIndex() {//初始响应的位置return 0;}public boolean returnOnMainThread() {//在主线程中返回return true;}/*** 持续进度显示** @param bytesWritten  当前输出大小* @param totalByte 总文件大小* @param progress      上传进度0~100*/public abstract void onProgress(long bytesWritten, long totalByte, int progress);}public ProgressRequestBody(RequestBody requestBody, Progress progress) {this.mRequestBody = requestBody;this.mProgress = progress;//判断是否是文件表单提交if (mRequestBody instanceof MultipartBody) {MultipartBody multipartBody = (MultipartBody) mRequestBody;for (MultipartBody.Part part : multipartBody.parts()) {if (null != part.body().contentType()) { //普通参数设置是没有contentType的try {totalByte += part.body().contentLength();//累计总提交的文件大小} catch (IOException e) {e.printStackTrace();}}}}}@Overridepublic MediaType contentType() {return mRequestBody.contentType();}//包装完成的BufferedSinkprivate BufferedSink bufferedSink;@Overridepublic void writeTo(BufferedSink sink) throws IOException {if (bufferedSink == null) {//包装bufferedSink = Okio.buffer(new ForwardingSink(sink) {//当前写入字节数long bytesWritten = 0L;@Overridepublic void write(Buffer source, long byteCount) throws IOException {super.write(source, byteCount);if (null != mProgress) {//增加当前写入的字节数bytesWritten += byteCount;/*** 回调,最终bytesWritten肯定是大于totalByte的,查看源码可知输出的内容还包括其他的* 例如请求头数据,请求参数等等*/if (bytesWritten > totalByte) {bytesWritten = totalByte;}int progress = (int) (bytesWritten * 100 / totalByte);if (mProgress.returnOnMainThread()) {//将结果发送到UI线程中long[] obj = new long[]{bytesWritten, totalByte, progress};mHandler.sendMessage(mHandler.obtainMessage(200, obj));} else {//结果保持在子线程中回调mProgress.onProgress(bytesWritten, totalByte, progress);}}}});}//写入mRequestBody.writeTo(bufferedSink);//必须调用flush,否则最后一部分数据可能不会被写入bufferedSink.flush();}
}

单个文件上传进度监听

//监听上传进度
private void demo19() {File file = createFile("c.mp4");//构建文件bodyRequestBody body = RequestBody.create(MediaType.parse("text/x-markdown;charset=utf-8"), file);MultipartBody multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM) //必须设置,multipart/form-data 才支持文件上传.addFormDataPart("upload", file.getName(), body) //name是表单提交的name.addFormDataPart("username", "胜哥").addFormDataPart("age", "22").addFormDataPart("hobby", "吃").addFormDataPart("hobby", "喝").addFormDataPart("hobby", "玩").build();Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1").post(new ProgressRequestBody(multiBody, new ProgressRequestBody.Progress() {@Overridepublic void onProgress(long bytesWritten, long totalByte, int progress) {System.out.println("bytesWritten:"+bytesWritten+" totalByte:"+totalByte+" progress:"+progress);}})).build();OkHttpClient client = new OkHttpClient();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});}

多个文件同时提交,监听总进度

private void demo20() {File file1 = createFile("c.mp4");File file2 = createFile("b.jpg");RequestBody file1Body = RequestBody.create(MediaType.parse("text/x-markdown;charset=utf-8"), file1);RequestBody file2Body = RequestBody.create(MediaType.parse("text/x-markdown;charset=utf-8"), file2);MultipartBody multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM) //必须设置,multipart/form-data 才支持文件上传.addFormDataPart("mulitupload", file1.getName(), file1Body) //name是表单提交的name.addFormDataPart("mulitupload", file2.getName(), file2Body) //name是表单提交的name.addFormDataPart("username", "胜哥").addFormDataPart("age", "22").addFormDataPart("hobby", "吃").addFormDataPart("hobby", "喝").addFormDataPart("hobby", "玩").build();Request request = new Request.Builder().url("http://192.168.30.217/shopping/test/demo1").post(new ProgressRequestBody(multiBody, new ProgressRequestBody.Progress() {@Overridepublic void onProgress(long bytesWritten, long totalByte, int progress) {System.out.println("bytesWritten:"+bytesWritten+" totalByte:"+totalByte+" progress:"+progress);}})).build();OkHttpClient client = new OkHttpClient();Call call = client.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {System.out.println("onResponse:" + response.body().string());}});
}

服务端代码实现

上面都是客户端的代码实现,下面介绍下服务端的代码,有需要的可以拿去用,服务器端用的是java实现的 ,使用的是Struts2框架,该框架的使用,大家如果不熟悉的话,可以先看看我博客专栏分类里面的Struts2栏目中的文章。

javabean

public class User {private String username;private int age;private List<String> hobby;private String image;private List<String> images;//get set 方法
}

Action类

package blog.csdn.net.mchenys.action;import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;import javax.servlet.http.HttpServletRequest;import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;import com.googlecode.sslplugin.annotation.Secured;
import com.opensymphony.xwork2.ModelDriven;import blog.csdn.net.mchenys.domain.User;
import blog.csdn.net.mchenys.utils.UploadUtils;/*** 测试*/
public class TestAction extends ActionSupport implements ModelDriven<User> {private static final long serialVersionUID = 1L;//模型驱动方式注入参数private User user = new User();@Overridepublic User getModel() {return user;}//处理响应格式为json的数据protected Map<String, Object> dataMap = new HashMap<String, Object>();//必须提供get方法public Map<String, Object> getDataMap() {return dataMap;}/**单文件上传* 通过属性驱动注入3个值 文件上传相关的3个属性 ,struts2会自动注入 * 上传的文件 : 属性名与表单中file的name属性名一致* 上传的文件名:属性名前段是name属性名一致+FileName; * 上传的文件类型: 属性名前段是name属性名一致 + ContentType;*/private File upload; // 表单的name是uploadprivate String uploadFileName; // 文件名private String uploadContentType;// 文件类型public void setUpload(File upload) {this.upload = upload;}public void setUploadFileName(String uploadFileName) {this.uploadFileName = uploadFileName;}public void setUploadContentType(String uploadContentType) {this.uploadContentType = uploadContentType;}/*** 批量文件上传,和单文件上传类似,只需要将那3个属性变成数组即可*/private File[] mulitupload; // 表单的name是mulituploadprivate String[] mulituploadFileName; // 文件名private String[] mulituploadContentType;// 文件类型public void setMulitupload(File[] mulitupload) {this.mulitupload = mulitupload;}public void setMulituploadFileName(String[] mulituploadFileName) {this.mulituploadFileName = mulituploadFileName;}public void setMulituploadContentType(String[] mulituploadContentType) {this.mulituploadContentType = mulituploadContentType;}// 文件下载private String fileName;// 文件类型private String downloadPath;//文件下载路径private String contentLength;//文件下载的长度/*** 返回文件的长度,对应struts.xml中的<param name="contentLength">contentLength</param>* @return*/public String getContentLength() {System.out.println("===getContentLength==="+contentLength);return contentLength;}public void setFileName(String fileName) {System.out.println("===setFileName==="+fileName);this.fileName = fileName;}/*** 返回文件名,对应struts.mlx中的<param name="contentDisposition">中的filename=${fileName}* @return*/public String getFileName() {System.out.println("===getFileName==="+fileName);return fileName;}/*** 返回InputStream,对应struts.mlx中的<param name="inputName">inputStream</param>** @return*/public InputStream getInputStream() {System.out.println("====getInputStream====");try {//转换格式,否则输出的文件名有中文时,浏览器不会显示this.fileName=new String(fileName.getBytes(),"iso-8859-1");return new FileInputStream(new File(downloadPath));} catch (Exception e) {e.printStackTrace();}return null;}/*** 文件下载* * @return*/public String download() {System.out.println("====download====");// 获取绝对路径this.downloadPath = ServletActionContext.getServletContext().getRealPath("/download/"+fileName);//指定文件长度(可选)this.contentLength = String.valueOf(new File(downloadPath).length());System.out.println("downloadPath:"+downloadPath);/*try {// 解决下载的文件名是中文的文件获取文件名时乱码问题,如果已经配置过编码拦截器的可以不需要处理this.downloadPath = new String(downloadPath.getBytes("ISO-8859-1"), "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}*/return "downloadOK";}public String demo1() throws Exception {HttpServletRequest request = ServletActionContext.getRequest();String cookie = request.getHeader("Cookie");if (null != this.uploadFileName) { //说明是单文件上传// 说明有文件需要上传String filename = UploadUtils.getUUIDName(this.uploadFileName);// 保存到tomcat目录下String uploadDidr = "E:\\apache-tomcat-7.0.52\\webapps\\upload";// 保存上传的文件,通过apache提供的工具类来操作FileUtils.copyFile(upload, new File(uploadDidr, filename));// 保存文件到User中,只保存相对路径,外部访问的时候可以拼上tomcat服务器的地址String image = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()+ "/upload/" + filename;user.setImage(image);}if (null != this.mulituploadFileName) { // 批量文件上传// 保存到tomcat目录下String uploadDidr = "E:\\apache-tomcat-7.0.52\\webapps\\upload";//保存上传的文件浏览路径List<String> images = new ArrayList<>();//循环读写文件for (int i = 0; i < mulituploadFileName.length; i++) {//uuid处理后的文件名String filename = UploadUtils.getUUIDName(mulituploadFileName[i]);//需要上传的文件File file = mulitupload[i];// 保存上传的文件,通过apache提供的工具类来操作FileUtils.copyFile(file, new File(uploadDidr, filename));// 保存文件到User中,只保存相对路径,外部访问的时候可以拼上tomcat服务器的地址String image = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()+ "/upload/" + filename;images.add(image);user.setImages(images);}}if (user.getHobby() != null) {List<String> hobbys = new ArrayList<>();for (String hobby : user.getHobby()) {if (hobby.contains(",")) { // 解决按,拼接提交的数据String[] split = hobby.split(",");hobbys.addAll(Arrays.asList(split));}}if (hobbys.size() > 0) {user.setHobby(hobbys);}}//输出json数据dataMap.put("status", 0);dataMap.put("msg", "请求成功");dataMap.put("user", user);return SUCCESS;}public String demo2() throws Exception {System.out.println("demo2 start");String content = null;BufferedReader br = new BufferedReader(new InputStreamReader(getRequest().getInputStream()));String line = null;StringBuilder sb = new StringBuilder();while ((line = br.readLine()) != null) {sb.append(line);}content = sb.toString();System.out.println("content=" + content);dataMap.put("content", content);return SUCCESS;}}

EncodingIntereptor

处理get和post请求参数乱码问题

package blog.csdn.net.mchenys.intercept;import java.io.UnsupportedEncodingException;
import java.util.Iterator;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsStatics;import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
/*** 解决请求乱码问题* @author mChenys**/
public class EncodingIntereptor extends AbstractInterceptor {private static final long serialVersionUID = 6826256332417695666L;@Overridepublic String intercept(ActionInvocation invo) throws Exception {HttpServletRequest request = ServletActionContext.getRequest();HttpServletResponse response = ServletActionContext.getResponse();if (request.getMethod().equalsIgnoreCase("post")) {try {request.setCharacterEncoding("utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}} else {Iterator<String[]> iterval = request.getParameterMap().values().iterator();while (iterval.hasNext()) {String[] parames = iterval.next();for (int i = 0; i < parames.length; i++) {try {parames[i] = new String(parames[i].getBytes("iso8859-1"), "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}}response.setContentType("text/html;charset=UTF-8");response.setHeader("Cache-Control", "no-cache");return invo.invoke();}}

UploadUtils

package blog.csdn.net.mchenys.utils;import java.util.UUID;/*** 文件上传的工具类* @author Administrator*/
public class UploadUtils {/*** 传入文件的名称,返回的唯一的名称* 例如:gril.jpg  返回sdjsljfsjdl.jpg* @param filename* @return*/public static String getUUIDName(String filename){// 先查找int index = filename.lastIndexOf(".");// 截取String lastname = filename.substring(index, filename.length());// 唯一 字符串  fsd-sfsdf-sfsd-sdfsdString uuid = UUID.randomUUID().toString().replace("-", "");return uuid+lastname;}public static void main(String[] args) {String filename = "girl.jpg";String uuid = getUUIDName(filename);System.out.println(uuid);}
}

struts.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN""http://struts.apache.org/dtds/struts-2.3.dtd"><struts><!-- 设置文件上传的大小 视频文件上传最大为100M --><constant name="struts.multipart.maxSize" value="104857600"></constant><package name="test" extends="ssl-default,json-default"namespace="/test"><!-- 自定义结果类型 --><result-types><result-type name="mulitStream" class="blog.csdn.net.mchenys.result.MulitStreamResult"></result-type></result-types><!-- 自定义拦截器 --><interceptors><!-- 声明自定义的编码处理拦截器 --><interceptor name="encoding"class="blog.csdn.net.mchenys.intercept.EncodingIntereptor" /><!-- 定义拦截器栈 --><interceptor-stack name="myStack"><!-- 引入自定义的拦截器 --><interceptor-ref name="encoding" /><!-- 引入默认的拦截器 --><interceptor-ref name="defaultStack" /></interceptor-stack></interceptors><action name="*" class="blog.csdn.net.mchenys.action.TestAction"method="{1}"><!-- 引入拦截器栈 --><interceptor-ref name="myStack" /><!-- json数据返回类型,需要额外引入struts2-json-plugin-2.3.24.jar包 --><result type="json"><param name="root">dataMap</param><!-- 是否去掉返回值为NULL的properties --><param name="excludeNullProperties">true</param></result><!-- 普通文件下载 --><result name="downloadOK" type="stream"><!-- result类型是流(stream)类型 --><!-- inputName指向被下载文件的来源,对应Action中返回的InputStream --><param name="inputName">inputStream</param><!-- 指定文件下载的处理方式,内联(inline)和附件(attachment)两种方式,attachment会弹出文件保存对话框 --><param name="contentDisposition">attachment;filename=${fileName}</param><!-- 文件大小 --><param name="contentLength">contentLength</param><!--指定下载文件的缓冲大小 --><param name="bufferSize">4096</param></result></action></package></struts>

搞定~~

OkHttp3使用介绍相关推荐

  1. okhttp3宏观介绍及基础回顾

    一.简介 1.1 okhttp是什么?    okhttp是由美国square [square是什么] 公司开发的,用于安卓和java应用程序的 HTTP+HTTP/2的客户端. 1.2 最新的版本 ...

  2. OkHttp协议介绍以及文件下载和上传+OkHttp协议封装+OkHttp拦截器____SpringBoot——集成Okhttp3

    OkHttp协议 okhttp是一个第三方类库,用于android中请求网络 这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso和LeakCan ...

  3. OkHttp3介绍(1)

    概述 OkHttp现在应该算是最火的Http第三方库,Retrofit底层也是使用OkHttp,网上很多教程都写的不错,但是有些我认为重要的知识,大多一笔带过,所以我决定写一篇入门文章 OkHttp官 ...

  4. 添加okhttp+android+studio,OKHTTP3 简单使用(一) 介绍及Android Studio集成

    HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽.OkHttp是一个高效的HTTP客户端,它有以下默认特性: 支持HTTP/2,允许所有同一个主机地址 ...

  5. OkHttp3 中几个拦截器基本功能介绍

    RetryAndFollowUpInterceptor 功能:实现重试.跟踪 实现原理: while(true) 死循环的实现. 检验返回的 Response ,如果没有异常(包括请求失败.重定向等) ...

  6. OKHTTP3源码和设计模式(下篇)

    ​ 在<OKHTTP3源码和设计模式(上篇)>,中整体介绍了 OKHttp3 的源码架构,重点讲解了请求任务的分发管理和线程池以及请求执行过程中的拦截器.这一章我们接着往下走认识一下 OK ...

  7. Okhttp、Volley和Gson的简单介绍和配合使用

    1.okhttp是一个高效的.快速的被谷歌认可的,支持HTTP/2和SPDY volley是一个方便网络任务库,可以负责请求.加载.缓存等同步问题,也可以处理图片.JSON.文本操作起来比较简单 gs ...

  8. Android OkHttp3简介和使用详解

    一 OKHttp简介 OKHttp是一个处理网络请求的开源项目,Android 当前最火热网络框架,由移动支付Square公司贡献,用于替代HttpUrlConnection和Apache HttpC ...

  9. Skywalking-01:Skywalking介绍

    Skywalking介绍 Application performance monitor tool for distributed systems, especially designed for m ...

  10. okHttp3 源码分析

    一, 前言 在上一篇博客OkHttp3 使用详解里,我们已经介绍了 OkHttp 发送同步请求和异步请求的基本使用方法. OkHttp 提交网络请求需要经过这样四个步骤: 初始化 OkHttpClie ...

最新文章

  1. [JAVA EE] Thymeleaf 常用工具类
  2. 浅析网站优化的站长们应该怎样安排自己的工作内容呢?
  3. ASP.NET夜话笔记06
  4. 通过先序和中序数组生成后续数组
  5. 路飞学城Python-Day11
  6. 下 终端_Linux/UNIX 下终端复用利器 tmux
  7. 面向对象三大特征之继承(extends)——Java笔记(六)
  8. golang unrecognized import path golang.org/x/net 完美解决方案
  9. python实现编辑距离,最长公共子序列,最长公共子串
  10. 基于人脸识别的课堂签到管理系统(五)---启动/结束签到,以及在百度智能云创建用户组
  11. DappSo周榜丨Dapp整体交易额呈下降趋势
  12. 2017.10.16 模拟赛
  13. 计算机管理系统工具共享文件夹,win7一键共享工具【管理方式】
  14. 数据挖掘:实用案例分析 下载_地下室防水施工技术及缺陷案例分析 | PPT下载
  15. SQL Server中以星期一为每周第一天 计算周数
  16. 关于ubuntu上,usb设备编号
  17. nm命令和其内容详解
  18. 优麒麟桌面闪烁_UKUI 桌面环境登陆 Arch Linux!
  19. Pandoc下载安装教程
  20. [NOIP2005提高组]过河

热门文章

  1. 微信公众号回复消息换行符处理
  2. 国内外GIS基础软件对比分析优缺特性及实际工作生产应用和成功案例综合评价
  3. Python实现高级电影特效,CXK也能影分身
  4. 【基于物理的渲染(PBR)白皮书】(三) 迪士尼原则的BRDF与BSDF相关总结
  5. layui模板引擎不生效解决方案
  6. 95%以上的日常办事启用电子签章,你都体验过哪些?
  7. python3 英文字母大小写的转变
  8. git创建本地ssh密匙
  9. Oracle错误处理机制
  10. 几大ERP软件实施方法与过程