Android 文件上传(包括大文件上传)
1.简介:
android 文件上传可以分为两类:一个是小文件,直接上传文件;一个是大文件,这个需要分块上传。Okhttp+Retrofit实现文件上传。
2. 需要的依赖和权限:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.2.5'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
3.示例:
3.1.小文件上传:直接上传文件(图片上传为例)
public class UpLoadImageUtils {
private static final String TAG = "UpLoadImageUtils";
//需要上传的图片数量
private static int imgSum;
//上传成功的图片数量
private static int uploadSuccessNum;
private static String enRttId;
//失败数量
private static int errorNum;
private static TestService apiService;
public static void getService(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
apiService = retrofit.create(TestService.class);
}
@SuppressLint("CheckResult")
public static void uploadImage(String url, File file) {
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM);//表单类型
Map<String, RequestBody> map = new HashMap<>();
//"image/png" 是内容类型,后台设置的类型
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
builder.addFormDataPart("name", file.getName());
builder.addFormDataPart("size", "" + file.length());
/*
* 这里重点注意:
* com_img[]里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
* filename是文件的名字,包含后缀名的 比如:abc.png
*/
builder.addFormDataPart("file", file.getName(), requestBody);
MultipartBody body = builder.build();
Observable<BaseResponse> meSetIconObservable = apiService.imgUpload(url, body);
meSetIconObservable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(baseResponse -> {
Log.i(TAG, JsonUtil.jsonToString(baseResponse));
if ("000000".equalsIgnoreCase(baseResponse.getCode())) {
Log.i(TAG, "onComplete: ---" + uploadSuccessNum);
errorNum = 0;
uploadSuccessNum++;
if (imgSum == uploadSuccessNum) {
finishUpload(true);
uploadSuccessNum = 0;
}
} else {
if (errorNum < 4) {
uploadImage(url, file);
errorNum++;
}else {
finishUpload(false);
}
}
}, throwable -> {
Log.i(TAG, "onComplete: ---" + throwable.getMessage());
});
}
/**
* 上传
*
* @param compressFile 需要上传的文件
* @param urls 需要上传的文件地址
*/
public static void uploadList(List<String> urls, List<File> compressFile) {
getService();
//多张图片
imgSum = urls.size();
for (int i = 0; i < compressFile.size(); i++) {
uploadImage(urls.get(i), compressFile.get(i));
}
}
public interface TestService {
@POST()
Observable<BaseResponse> imgUpload(@Url String url, @Body MultipartBody multipartBody);
}
}
public class UpLoadImageUtils {
private static final String TAG = "UpLoadImageUtils";
//需要上传的图片数量
private static int imgSum;
//上传成功的图片数量
private static int uploadSuccessNum;
private static String enRttId;
//失败数量
private static int errorNum;
private static TestService apiService;
public static void getService(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
apiService = retrofit.create(TestService.class);
}
@SuppressLint("CheckResult")
public static void uploadImage(String url, File file) {
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM);//表单类型
Map<String, RequestBody> map = new HashMap<>();
//"image/png" 是内容类型,后台设置的类型
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
builder.addFormDataPart("name", file.getName());
builder.addFormDataPart("size", "" + file.length());
/*
* 这里重点注意:
* com_img[]里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
* filename是文件的名字,包含后缀名的 比如:abc.png
*/
builder.addFormDataPart("file", file.getName(), requestBody);
MultipartBody body = builder.build();
Observable<BaseResponse> meSetIconObservable = apiService.imgUpload(url, body);
meSetIconObservable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(baseResponse -> {
Log.i(TAG, JsonUtil.jsonToString(baseResponse));
if ("000000".equalsIgnoreCase(baseResponse.getCode())) {
Log.i(TAG, "onComplete: ---" + uploadSuccessNum);
errorNum = 0;
uploadSuccessNum++;
if (imgSum == uploadSuccessNum) {
finishUpload(true);
uploadSuccessNum = 0;
}
} else {
if (errorNum < 4) {
uploadImage(url, file);
errorNum++;
}else {
finishUpload(false);
}
}
}, throwable -> {
Log.i(TAG, "onComplete: ---" + throwable.getMessage());
});
}
/**
* 上传
*
* @param compressFile 需要上传的文件
* @param urls 需要上传的文件地址
*/
public static void uploadList(List<String> urls, List<File> compressFile) {
getService();
//多张图片
imgSum = urls.size();
for (int i = 0; i < compressFile.size(); i++) {
uploadImage(urls.get(i), compressFile.get(i));
}
}
public interface TestService {
@POST()
Observable<BaseResponse> imgUpload(@Url String url, @Body MultipartBody multipartBody);
}
}
3.2.大文件分块上传(视频上传为例)同步
public class UploadMediaFileUtils {
private static final String TAG = "UploadMediaFileUtils";
private static UploadService uploadService;
//基础的裁剪大小20m
private static final long baseCuttingSize = 20 * 1024 * 1024;
//总的块数
private static int sumBlock;
//取消上传
private static boolean isCancel = false;
//是否在上传中
private static boolean isUploadCenter=false;
public static void uploadMediaFile(String url, String uploadName, File file, String appInfo, IOUploadAudioListener ioResultListener) {
if (file.exists()) {
getService();
//总的分块数
sumBlock = (int) (file.length() / baseCuttingSize);
if (file.length() % baseCuttingSize != 0) {
sumBlock = sumBlock + 1;
}
isCancel = false;
isUploadCenter = true;
uploadMedia(url, uploadName, file, appInfo, 1, ioResultListener);
} else {
Log.i(TAG, "文件不存在");
ioResultListener.errorResult("-1", "文件不存在");
}
}
@SuppressLint("CheckResult")
public static void uploadMedia(String url, String uploadName, File file, String appInfo, int currentBlock, IOUploadAudioListener ioResultListener) {
if (isCancel){
Log.i(TAG, "取消上传");
return;
}
byte[] fileStream = cutFile(file, currentBlock - 1, ioResultListener);
if (fileStream == null) {
Log.i(TAG, "uploadMedia: getBlock error");
ioResultListener.errorResult("-1", "fileStream为空");
return;
}
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM);//表单类型
RequestBody requestBody = RequestBody.create(MultipartBody.FORM, fileStream);
builder.addFormDataPart("name", uploadName);
builder.addFormDataPart("size", "" + fileStream.length);
Log.i(TAG, "size" + fileStream.length);
builder.addFormDataPart("num", "" + currentBlock);
/*
* 这里重点注意:
* com_img[]里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
* filename是文件的名字,包含后缀名的 比如:abc.png
*/
builder.addFormDataPart("file", file.getName(), requestBody);
MultipartBody body = builder.build();
Observable<BaseResponse> meSetIconObservable = uploadService.mediaUpload("huizhan", appInfo, url, body);
meSetIconObservable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(baseResponse -> {
if ("000000".equalsIgnoreCase(baseResponse.getCode())) {
double progress = 100 * div(currentBlock, sumBlock, 2);
ioResultListener.progress("" + progress);
if (currentBlock < sumBlock) {
uploadMedia(url, uploadName, file, appInfo, currentBlock + 1, ioResultListener);
return;
}
ioResultListener.successResult(baseResponse);
} else {
ioResultListener.errorResult(baseResponse.getCode(), baseResponse.getDesc());
}
}, throwable -> {
ioResultListener.errorResult("-1", "上传失败");
});
}
public static void getService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
uploadService = retrofit.create(UploadService.class);
}
public interface UploadService {
@POST()
Observable<BaseResponse> mediaUpload(@Header("X-Biz-Id") String bizId,
@Header("X-App-Info") String AppInfo,
@Url String url,
@Body MultipartBody multipartBody);
}
/**
* 写入本地(测试用)
*
* @param list
*/
public static void writeFile(List<byte[]> list) {
FileWriter file1 = new FileWriter();
String path = Environment.getExternalStorageDirectory() + File.separator + "12345.wav";
try {
file1.open(path);
for (int i = 0; i < list.size(); i++) {
Log.i(TAG, "writeFile: " + list.get(i).length);
file1.writeBytes(list.get(i), 0, list.get(i).length);
}
file1.close();
LogUtils.i(TAG, "writeFile: ");
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] cutFile(File file, int currentBlock, IOUploadAudioListener ioResultListener) {
Log.i(TAG, "getBlockThree:000000---" + currentBlock);
int size = 20 * 1024 * 1024;
byte[] endResult = null;
byte[] result = new byte[size];
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
RandomAccessFile accessFile = new RandomAccessFile(file, "rw");
accessFile.seek(currentBlock * size);
//判断是否整除
if (file.length() % baseCuttingSize != 0) {
//当前的位数和总数是否相等(是不是最后一段)
if ((currentBlock + 1) != sumBlock) {
int len = accessFile.read(result);
out.write(result, 0, len);
endResult = out.toByteArray();
} else {
//当有余数时
//当前位置2147483647-20971520
byte[] bytes = new byte[(int) (file.length() % baseCuttingSize)];
int len = accessFile.read(bytes);
out.write(bytes, 0, len);
endResult = out.toByteArray();
}
} else {
int len = accessFile.read(result);
out.write(result, 0, len);
endResult = out.toByteArray();
}
accessFile.close();
out.close();
} catch (IOException e) {
// e.printStackTrace();
ioResultListener.errorResult("-1", "cutFile失败");
}
return endResult;
}
/**
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
* 定精度,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 取消上传
*/
public static void cancelUpload() {
if (isUploadCenter) {
isCancel = true;
}
}
作者:zhang106209
链接:https://juejin.cn/post/7234700513223557180
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
链接:https://juejin.cn/post/7234700513223557180
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。