1.文件上传,post提交表单
1.1表单提交需要用到 post,put方法,以方法体的方式组件表单信息。
RequestBody requestBody = new RequestBody()
.type(RequestBody.FORM)
.addParam("file", RequestBody.create(file))
.addParam("file2", RequestBody.create(file))
.addParam("pageSize", 1 + "");
1.2 然后在RequestBody 中将添加的参数添加到一个集合之中
public RequestBody addParam(String key, String value) {
params.put(key,value);
return this;
}
public RequestBody addParam(String key, Bindry value) {
params.put(key,value);
return this;
}
1.3 确定文件传输的类型
public RequestBody type(String type) {
this.type = type;
return this;
}
1.4 创建文件,获取创建的文件的长度,类型,文件名,
//获取创建的文件的长度,类型,文件名
public static Bindry create(final File file) {
return new Bindry() {
@Override
public long fileLength() {
return file.length();
}
@Override
public String mimType() {
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimType = fileNameMap.getContentTypeFor(file.getAbsolutePath());
if(TextUtils.isEmpty(mimType)){
return "application/octet-stream";
}
return mimType;
}
@Override
public String fileName() {
return file.getName();
}
@Override
public void onWrite(OutputStream outputStream) throws IOException {
InputStream inputStream = new FileInputStream(file);
byte[] buffer = new byte[2048];
int len = 0;
while ((len=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
inputStream.close();
}
};
}
1.5将RequestBody 存到Request中,
Request request = new Request.Builder()
.url("https://api.saiwuquan.com/api/appv2/sceneModel")
.post(requestBody).build();
1.6封装成一个请求之后返回给MainActivity(调用者)
public Request build(){
return new Request(this);
}
1.7 通过 OkhttpCliet的newInstance(request) 这个方法,将将请求 封装成一个Call在返回给客户端(MainActivity)
Call call = client.newCall(request);
1.8 Call 的 newInstance(request) 方法实际上是调用RealCall的newInstance(request,this) ,RealCall是接口Call的实现类,所以返回值可以用以个Call接收,并同时将封装好的request和网络引擎OkhttpCliet客户端传递给RealCall。
public Call newCall(Request request) {
return RealCall.newCall(request,this);
}
1.9 RealCall 内部对Call的封装
public static Call newCall(Request request, OkHttpClient client) {
return new RealCall(request,client);
}
public RealCall(Request request, OkHttpClient client) {
orignalRequest = request;
this.client = client;
}
1.10 通过Call的enqueue这个方法去异步的执行网络请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("TAG", response.string());
//Log.e("TAG",response.string());
}
});
1.11但是因为Call是一个接口,所以当调用enqueue这个方法的时候,就会执行Call的实现类RealCall的enqueue方法
@Override
public void enqueue(Callback callback) {
// 异步的
AsyncCall asyncCall = new AsyncCall(callback);
// 交给线程池
client.dispatcher.enqueue(asyncCall);
}
1.12 AsyncCall 是RealCall的一个内部类,它是NamedRunnable的子类,覆写NamedRunnable的抽象方法exectue , 而NamedRunable是接口Runnable的实现类,并在run方法内调用了exectue,最终会调到AsyncCall的exectue这个方法
public abstract class NamedRunnable implements Runnable{
@Override
public void run() {
execute();
}
protected abstract void execute();
}
final class AsyncCall extends NamedRunnable {
Callback callback;
public AsyncCall(Callback callback) {
this.callback = callback;
}
@Override
protected void execute() {
// 来这里,开始访问网络 Request -> Response
Log.e("TAG", "execute");
// Volley xUtils Afinal AsyHttpClient
// 基于 HttpUrlConnection , OkHttp = Socket + okio(IO)
final Request request = orignalRequest;
try {
URL url = new URL(request.url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if (urlConnection instanceof HttpsURLConnection) {
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) urlConnection;
// https 的一些操作
// httpsURLConnection.setHostnameVerifier();
// httpsURLConnection.setSSLSocketFactory();
}
// urlConnection.setReadTimeout();
// 写东西
urlConnection.setRequestMethod(request.method.name);
urlConnection.setDoOutput(request.method.doOutput());
RequestBody requestBody = request.requestBody;
if (requestBody != null) {
// 头信息
//设置传输文件的格式
urlConnection.setRequestProperty("Content-Type", requestBody.getContentType());
//设置我们将传输多少长度的信息
urlConnection.setRequestProperty("Content-Length", Long.toString(requestBody.getContentLength()));
}
urlConnection.connect();
// 写内容
if (requestBody != null) {
requestBody.onWriteBody(urlConnection.getOutputStream());
}
int statusCode = urlConnection.getResponseCode();
if (statusCode == 200) {
InputStream inputStream = urlConnection.getInputStream();
Response response = new Response(inputStream);
callback.onResponse(RealCall.this, response);
}
// 进行一些列操作,状态码 200
}catch (IOException e){
callback.onFailure(RealCall.this, e);
}
}
}
1.13 至于Dispatcher这个方法 这个类在Okhttpcliet中被实例化,通过这行代码将线程AsyncCall这个任务添加到线程池当中去。
client.dispatcher.enqueue(asyncCall);
1.14用dispatcher 的 enqueue这个方法将AsyncCall这个任务添加到线程池中
public void enqueue(RealCall.AsyncCall call) {
executorService().execute(call);
}
1.15 当调用dispatcher 的 enqueue这个方法将AsyncCall这个任务添加到线程池中的时候,在这个方法内部会调用executorService这个方法,开启线程池,
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable r) {
Thread thread = new Thread(r,"OkHttp");
thread.setDaemon(false);
return thread;
}
});
}
return executorService;
}
1.16 然后线程池就回执行NamedRunnable的run方法,run方法会去执行AsyncCall的execute这个方法,在这个方法中开始真正的请求网络的操作,由于AsyncCall是RealCall的内部类,而RealCall又封装了request,request中封装了 url,requestBody,等这些东西。
final class AsyncCall extends NamedRunnable {
Callback callback;
public AsyncCall(Callback callback) {
this.callback = callback;
}
@Override
protected void execute() {
// 来这里,开始访问网络 Request -> Response
Log.e("TAG", "execute");
// Volley xUtils Afinal AsyHttpClient
// 基于 HttpUrlConnection , OkHttp = Socket + okio(IO)
final Request request = orignalRequest;
try {
URL url = new URL(request.url);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if (urlConnection instanceof HttpsURLConnection) {
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) urlConnection;
// https 的一些操作
// httpsURLConnection.setHostnameVerifier();
// httpsURLConnection.setSSLSocketFactory();
}
// urlConnection.setReadTimeout();
// 写东西
urlConnection.setRequestMethod(request.method.name);
urlConnection.setDoOutput(request.method.doOutput());
RequestBody requestBody = request.requestBody;
if (requestBody != null) {
// 头信息
//设置传输文件的格式
urlConnection.setRequestProperty("Content-Type", requestBody.getContentType());
//设置我们将传输多少长度的信息
urlConnection.setRequestProperty("Content-Length", Long.toString(requestBody.getContentLength()));
}
urlConnection.connect();
// 写内容
if (requestBody != null) {
requestBody.onWriteBody(urlConnection.getOutputStream());
}
int statusCode = urlConnection.getResponseCode();
if (statusCode == 200) {
InputStream inputStream = urlConnection.getInputStream();
Response response = new Response(inputStream);
callback.onResponse(RealCall.this, response);
}
// 进行一些列操作,状态码 200
}catch (IOException e){
callback.onFailure(RealCall.this, e);
}
}
}
1.17 需要将请求头里面的东西写道HttpUrlConnection中,首先设置请求方式 urlConnection.setRequestMethod(request.method.name)再开启输出流urlConnection.setSoOutput(request.method.doOutput),判断requestBody不为空的话,就设置请求的请求参数(传输文本的类型和传输文本的长度),再次判断请求头是否为空调用请求头内部封装的方法onWriteBody并将HttpUrlConnettion的输出流传进去
// 写东西
urlConnection.setRequestMethod(request.method.name);
urlConnection.setDoOutput(request.method.doOutput());
RequestBody requestBody = request.requestBody;
if (requestBody != null) {
// 头信息
//设置传输文件的格式
urlConnection.setRequestProperty("Content-Type", requestBody.getContentType());
//设置我们将传输多少长度的信息
urlConnection.setRequestProperty("Content-Length", Long.toString(requestBody.getContentLength()));
}
urlConnection.connect();
// 写内容
if (requestBody != null) {
requestBody.onWriteBody(urlConnection.getOutputStream());
}
1.18 再onWriteBody这个方法遍历装有请求头参数的集合,并且按照一定的格式拼接成字符串,然后写到urlConnection的输出流里面,如果是Bindry这个类型的,就将文本输入,再将输出流传入到bindry的onWrite中将文件中的内容写入到输出流当中去。
public void onWriteBody(OutputStream outputStream) throws IOException{
for(Map.Entry<String,Object> entry:params.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if(value instanceof String){
String postTextStr = getText(key,(String)value);
outputStream.write(postTextStr.getBytes());
}
if(value instanceof Bindry){
Bindry bindry = (Bindry) value;
String postTextStr = getText(key,bindry);
outputStream.write(postTextStr.getBytes());
bindry.onWrite(outputStream);
outputStream.write("\r\n".getBytes());
}
}
if(params.size()!=0){
outputStream.write(endBoundary.getBytes());
}
}
@Override
public void onWrite(OutputStream outputStream) throws IOException {
InputStream inputStream = new FileInputStream(file);
byte[] buffer = new byte[2048];
int len = 0;
while ((len=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
inputStream.close();
}
1.19 首先再addParam(key, bindry)这个方法中 通过RequestBody.create这个方法将文件的长度,类型,名称封装成bindry这个是实体类,存放在集合当中,所以当在onWriteBody这个方法当中取出bindry之后,就可以访问他的属性了。
public interface Bindry {
long fileLength();
String mimType();
String fileName();
void onWrite(OutputStream outputStream)throws IOException;
}
2.文件提交
2.1 文件提交格式
startBoundary+"\r\n"+
"Content-Disposition: form-data; name = \""+key+"\" filename = \""+value.fileName()+"\""+
"Context-Type: "+value.mimType()+"\r\n"+
"\r\n";