一、 导入
- 回忆学过的网络请求:OkHttp(基本步骤)、Retrofit。
- 上传文件接口讲解
二、上传文件
1. ok上传
- 添加依赖
//glide
implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
//okhttp
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
//gson
implementation 'com.google.code.gson:gson:2.2.4'
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0' // 必要依赖,解析json字符所用
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' // 必要依赖,和Rxjava结合必须用到,下面会提到
implementation "io.reactivex.rxjava2:rxandroid:2.0.1" // 必要rxandrroid依赖,切线程时需要用到
- 添加权限
- 布局(Button、TextView、Imageview),初始化id
- 动态权限获取
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
upLoadFile();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
}
- 上传文件upLoadFileByOk
private void updataFile() {
//要上传的文件
File file = new File(Environment.getExternalStorageDirectory() + "/Pictures/a.jpg");
//创建ok实例
OkHttpClient okClient = new OkHttpClient.Builder().build();
//封装文件格式及文件
final RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
//封装文件参数
MultipartBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("key", "H1810B")
.addFormDataPart("file", file.getName(), requestBody)
.build();
//构建请求
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
//ok客户端请求
okClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String string = response.body().string();
Gson gson = new Gson();
final UpdataFileBean updataFileBean = gson.fromJson(string, UpdataFileBean.class);
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText(updataFileBean.getRes());
if (updataFileBean != null && updataFileBean.getCode() == 200) {
Glide.with(MainActivity.this).load(updataFileBean.getData().getUrl()).into(imageview);
} else {
Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT).show();
}
}
});
}
});
}
2. Retrofit上传文件
接口:
public interface ApiService {
//http://yun.cn/study/public/file_upload.php
String mBaseUrl = "http://yun.cn/";
@Multipart
@POST("study/public/file_upload.php")
Observable upload(@Part("key")RequestBody key, @Part MultipartBody.Part file);
}
上传:
private void updataFileR() {
File file = new File(Environment.getExternalStorageDirectory() + "/Pictures/a.jpg");
//1.创建retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MyService.baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
//2.创建service对象
MyService myService = retrofit.create(MyService.class);
//3.service对象调用方法
//封装类型
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
//文件封装
MultipartBody.Part file1 = MultipartBody.Part.createFormData("file", file.getName(), requestBody);
//文本封装
RequestBody requestBody1 = RequestBody.create(MediaType.parse("multipart/form-data"), "H1810b");
Observable up = myService.upload(requestBody1, file1);
up.subscribeOn(Schedulers.io())//子线程请求
.observeOn(AndroidSchedulers.mainThread())//主线程操作
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(UpdataFileBean updataFileBean) {
tv.setText(updataFileBean.getRes());
if (updataFileBean != null && updataFileBean.getCode() == 200) {
Glide.with(MainActivity.this).load(updataFileBean.getData().getUrl()).into(imageview);
} else {
Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(Throwable e) {
Log.e("TAG", "onError:" + e.getMessage());
}
@Override
public void onComplete() {
Log.e("TAG", "onComplete:");
}
});
}
3. HttpUrlConnection上传文件
- 上传
/**
* HttpUrlConnection 实现文件上传
*
* @param params 普通参数
* @param fileFormName 文件在表单中的键
* @param uploadFile 上传的文件
* @param newFileName 文件在表单中的值(服务端获取到的文件名)
* @param urlStr url
* @throws IOException
*/
public void uploadForm(Map params, String fileFormName, File uploadFile, String newFileName, String urlStr) throws IOException {
if (newFileName == null || newFileName.trim().equals("")) {
newFileName = uploadFile.getName();
}
StringBuilder sb = new StringBuilder();
/**
* 普通的表单数据
*/
if (params != null) {
for (String key : params.keySet()) {
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n");
sb.append("\r\n");
sb.append(params.get(key) + "\r\n");
}
}
/**
* 上传文件的头
*/
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"" + fileFormName + "\"; filename=\"" + newFileName + "\""
+ "\r\n");
sb.append("Content-Type: application/octet-stream" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType
sb.append("\r\n");
byte[] headerInfo = sb.toString().getBytes("UTF-8");
byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
// 设置传输内容的格式,以及长度
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
conn.setRequestProperty("Content-Length",
String.valueOf(headerInfo.length + uploadFile.length() + endInfo.length));
conn.setDoOutput(true);
OutputStream out = conn.getOutputStream();
InputStream in = new FileInputStream(uploadFile);
//写入的文件长度
int count = 0;
//文件的总长度
int available = in.available();
// 写入头部 (包含了普通的参数,以及文件的标示等)
out.write(headerInfo);
// 写入文件
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
count += len;
int progress = count * 100 / available;
Log.d(TAG, "上传进度: " + progress + " %");
updateProgress(progress);
}
// 写入尾部
out.write(endInfo);
in.close();
out.close();
if (conn.getResponseCode() == 200) {
System.out.println("文件上传成功");
String s = stream2String(conn.getInputStream());
Log.d(TAG, "uploadForm: " + s);
}
}
// 分割符,自己定义即可
private static final String BOUNDARY = "----WebKitFormBoundaryT1HoybnYeFOGFlBR";
public String stream2String(InputStream is) {
int len;
byte[] bytes = new byte[1024];
StringBuffer sb = new StringBuffer();
try {
while ((len = is.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len));
}
is.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private void updateProgress(final int progress) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mPb.setProgress(progress);
}
});
}
//调用
new Thread(new Runnable() {
@Override
public void run() {
try {
HashMap map = new HashMap<>();
map.put("key", "1908A");
uploadForm(map, "file", file, file.getName(), "http://yun918.cn/");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
- 线程池
public class ThreadPoolUtils {
private static MyThreadUtils myThreadUtils;
private final ThreadPoolExecutor executor;
private ThreadPoolUtils(){
executor = new ThreadPoolExecutor(5,//核心线程数量,核心池的大小
20,//线程池最大线程数
30,//表示线程没有任务执行时最多保持多久时间会终止
TimeUnit.SECONDS,//时间单位
new LinkedBlockingQueue(),//任务队列,用来存储等待执行的任务
Executors.defaultThreadFactory(),//线程工厂,如何去创建线程的
new ThreadPoolExecutor.AbortPolicy());
}
public static MyThreadUtils getMyThreadUtils() {
if (myThreadUtils == null){
synchronized (MyThreadUtils.class){
if (myThreadUtils == null){
myThreadUtils = new MyThreadUtils();
}
}
}
return myThreadUtils;
}
public void excecute(Runnable runnable){
if (runnable != null) {
executor.execute(runnable);
}
}
public void remove(Runnable runnable){
if (runnable != null) {
executor.remove(runnable);
}
}
public void shutdown(){
if (executor!=null){
executor.shutdown();
}
}
}
4. 拍照获取文件并上传
- 打开相机拍照并将图片返回上传到服务器:
//处理权限
private void takePhoto() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
openCamera();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA}, 200);
}
}
- Android 7.0 就是 File 路径的变更,需要使用 FileProvider 来做,下面看拍照的代码。
private void openCamera() {
//创建文件用于保存图片
mFile = new File(getExternalCacheDir(), System.currentTimeMillis() + ".jpg");
if (!mFile.exists()) {
try {
mFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//适配7.0
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
mImageUri = Uri.fromFile(mFile);
} else {
//第二个参数要和清单文件中的配置保持一致
mImageUri = FileProvider.getUriForFile(this, "com.baidu.upload.provider", mFile);
}
//启动相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);//将拍照图片存入mImageUri
startActivityForResult(intent, CAMERA_CODE);
}
- 获取拍照结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_CODE) {
if (resultCode == RESULT_OK) {
try {
//Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(mImageUri));
//处理照相之后的结果并上传
uploadOk(mFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 清单文件中配置FileProvider:
- 需要在res/xml下创建一个file_paths.xml文件
5. 相册选取照片并上传
- 开启相册并选择图片上传(注意权限处理):
private void openAlbum() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
startActivityForResult(intent,ALBUM_CODE);
}
- 意图回调获取图片
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_CODE) {
//相机
if (resultCode == RESULT_OK){
try {
//Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(mImageUri));
uploadOk(mFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}else if (requestCode == ALBUM_CODE){
//相册
if (resultCode == RESULT_OK){
Uri imageUri = data.getData();
//处理uri,7.0以后的fileProvider 把URI 以content provider 方式 对外提供的解析方法
File file = getFileFromUri(imageUri, this);
if (file.exists()){
uploadOk(file);
}
}
}
}
- 7.0以后的fileProvider 把URI 以content provider 方式 对外提供的解析方法
public File getFileFromUri(Uri uri, Context context) {
if (uri == null) {
return null;
}
switch (uri.getScheme()) {
case "content":
return getFileFromContentUri(uri, context);
case "file":
return new File(uri.getPath());
default:
return null;
}
}
/**
通过内容解析中查询uri中的文件路径
*/
private File getFileFromContentUri(Uri contentUri, Context context) {
if (contentUri == null) {
return null;
}
File file = null;
String filePath;
String[] filePathColumn = {MediaStore.MediaColumns.DATA};
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(contentUri, filePathColumn, null,
null, null);
if (cursor != null) {
cursor.moveToFirst();
filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
cursor.close();
if (!TextUtils.isEmpty(filePath)) {
file = new File(filePath);
}
}
return file;
}