天天看點

Android 檔案上傳(包括大檔案上傳)1.簡介:2. 需要的依賴和權限:3.示例:

1.簡介:

目錄

1.簡介:

2. 需要的依賴和權限:

3.示例:

3.1.小檔案上傳:直接上傳檔案(圖檔上傳為例)

3.2.大檔案分塊上傳(視訊上傳為例)同步

      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);
    }
}           

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;
        }
    }