文件上传

一、 导入

  • 回忆学过的网络请求: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依赖,切线程时需要用到 
  • 添加权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.ptermission.WRITE_EXTERNAL_STORAGE"/>
  • 布局(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<UploadBean> 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<UpdataFileBean> up = myService.upload(requestBody1, file1);
        up.subscribeOn(Schedulers.io())//子线程请求
                .observeOn(AndroidSchedulers.mainThread())//主线程操作
                .subscribe(new Observer<UpdataFileBean>() {
                    @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<String, String> 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<String, String> 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<Runnable>(),//任务队列,用来存储等待执行的任务
                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:
<provider
            android:authorities="com.baidu.upload.provider"
            android:name="android.support.v4.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">

            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>

        </provider>
  • 需要在res/xml下创建一个file_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path name="upload" path=""/>
</paths>

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;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容