基于Android分区存储下 使用okhttp上传图片与下载图片至相册(已封装好工具类)

关于分区存储

1、Android10加入了分区存储, Android11后会强制执行,执行的后果就是无法使用file来访问非应用包名目录下的文件。
2、只需要在清单文件中的application中加入这行android:requestLegacyExternalStorage="true"即可强制关闭分区存储(Android10有效 Android11不行),设为true则为开启分区存储(用来提前测试分区存储,开启后记得卸载app重新安装一下,不然有问题)。
我的测试机就是Android10的。

文件的uri地址是这种类型的:content://com.android.providers.media.documents/document/video%3A68638

利用okhttp上传单张图片

1、打开图片选择页面
首先是选择单张图片 看代码,通过意图intent来打开系统的相册选择界面,这个是Android官网的代码,选择单张图片。

private static final int READ_REQUEST_CODE = 42;
    /**
     * Fires an intent to spin up the "file chooser" UI and select an image.
     * 选取单个图片
     */
    public void  performFileSearch() {

        // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
        // browser.
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

        // Filter to only show results that can be "opened", such as a
        // file (as opposed to a list of contacts or timezones)
        intent.addCategory(Intent.CATEGORY_OPENABLE);

        // Filter to show only images, using the image MIME data type.
        // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
        // To search for all documents available via installed storage providers,
        // it would be "*/*".
        intent.setType("image/*");//定义了选择图片,可以换成别的MIME类型试试
        							//如将image换成video,则可以选择视频上传

        startActivityForResult(intent, READ_REQUEST_CODE);
    }

2、获取所选择图片的uri
选择好以后返回到activity中,这个时候在onActivityResult中会得到intent返回的选中的文件的uri

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode){
                case READ_REQUEST_CODE://上传一张图片
                    Uri uri;
                    if (data != null) {
                        uri = data.getData();//文件的uri地址
                        //获取到uri后可在这里执行上传操作  upload
                    }
                    break;
            }
        }
    }

3、获取文件在手机上的绝对路径
这个时候得到了文件的uri,那么,我们需要从uri中获取文件的名字,那么我们调用我们的FileUtils类来获取文件的绝对路径。

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode){
                case READ_REQUEST_CODE://上传一张图片
                    Uri uri;
                    if (data != null) {
                        uri = data.getData();//文件的uri地址
                        String path=FileUtils.getPath(this,uri);//获取文件绝对路径
                        //这个FileUtils类会在后面放上代码,现在只说文件的上传。
                    }
                    break;
            }
        }
    }

4、通过文件的绝对路径来获取文件名(带后缀)
直接将得到的路径放入file类中,虽然file类不能访问外部存储空间,但是可以通过这个file类来获取这个绝对路径文件对于的文件名及后缀
如xxxx.jpg

String fileName=new File(path).getName;//文件名   XXX.JPG

5、获取文件的mime类型,这个可以通过uri来获取,代码如下

String Mimetype = getContentResolver().getType(uri);

6、文件File的获取(无法获取) 看下一步
之后有了文件名 文件类型 ,那么还差一个文件File。
当然在分区存储中是无法用file的api来访问外部存储的,那么,我们就不能传递一个File类了

RequestBody requestBody = RequestBody.create(file, MediaType.parse(type));//我们无法使用file将图片写入相册

7、只能通过byte[] 字节流的方式来传递file文件了。我们可以通过ContentResolver来获取当前文件uri对于的输入流,所以将输入流转为字节流后再进行传输。这么一来就传输完成了。

//OkHttp上传单个文件
    void upload(Uri uri){
        InputStream inputStream=null;
        try {
            //client
            OkHttpClient client = new OkHttpClient.Builder()
                    .connectTimeout(10000, TimeUnit.MILLISECONDS)
                    .build();


            String path = FileUtils.getPath(this, uri);//通过uri得到了文件的绝对路径
            File file = new File(path);//特定新建一个file类
            String fileName = file.getName();//来获取文件名  主要是文件名的后缀
            inputStream = getContentResolver().openInputStream(uri);

            byte[] bytes = FileUtils.toByteArray(inputStream);

            String type = getContentResolver().getType(uri);
            RequestBody requestBody = RequestBody.create(bytes, MediaType.parse(type));
            MultipartBody multipartBody = new MultipartBody.Builder()
                    .addFormDataPart("file",fileName, requestBody)
                    .build();
            Request request = new Request.Builder()
                    .url(baseUrl+"/file/upload")
                    .post(multipartBody)
                    .build();
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull IOException e) {
                    Toast("请求失败"+e);
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                    Toast("成功了:"+response.body().string());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Log.e("TAG", "Uri: "+uri.toString() );
    }

工具类代码

FileUtils.class

package com.xunua.networkproject.Utils;
 
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


/**
 * @user XUNUA
 * @email  xunua@outlook.com
 * @creat time 2020/5/6 12:21
 */
public class FileUtils {
    private static String TAG = "FileUtils";
 
 
    /**
     * 专为Android4.4设计的从Uri获取文件绝对路径
     */
    @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {
 
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
 
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
 
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
 
                final String id = DocumentsContract.getDocumentId(uri);
                if (id != null && id.startsWith("raw:")) {
                    return id.substring(4);
                }
 
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                Log.w(TAG,contentUri+"");
 
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                Log.w(TAG,docId);
                Log.w(TAG,type);
 
                Uri contentUri = null;
 
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
                Log.e(TAG,"isMediaDocument");
 
                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{split[1]};
 
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            Log.e(TAG,"content");
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            Log.e(TAG,"file");
            return uri.getPath();
        }
        return null;
    }
 
    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context       The context.
     * @param uri           The Uri to query.
     * @param selection     (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                String[] selectionArgs) {
 
        Cursor cursor = null;
        Log.w(TAG,"hh:"+uri);
        final String column = "_data";
        final String[] projection = {column};
 
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            Log.w(TAG,"hh1:"+cursor);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    /**
     * 将inputStream转换成字节数组
     * @param input
     * @return
     * @throws IOException
     */
    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024*4];
        int length = 0;
        while (-1 != (length = input.read(buffer))) {
            Log.e(TAG, "toByteArray: "+length);
            output.write(buffer, 0, length);
        }
        output.close();
        input.close();
        return output.toByteArray();
    }

    /**
     *
     * @param context       上下文
     * @param fileName      文件的名称 xxx.jpg
     * @param inputStream   从网络中或者其他地方读取到的要写入的图片的输入流
     * @param Subfolders   可以理解为相册的名字 如MyPic       如果为null,则在相册的Pictures文件夹下存放图片
     * @return  boolean  true为存储成功 false为存储失败
     */
    public static boolean saveImage(Context context,String fileName,InputStream inputStream,String Subfolders){
        ContentResolver contentResolver = context.getContentResolver();
        Uri contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME,fileName);//文件名
        contentValues.put(MediaStore.Images.Media.MIME_TYPE,"image/*");//文件类型
        if (Subfolders == null) {
            Subfolders="";
        }
        contentValues.put(MediaStore.Images.Media.RELATIVE_PATH,"Pictures/"+Subfolders);
        Uri insert = contentResolver.insert(contentUri, contentValues);
        if (insert == null) {
            Log.e("TAG", "onResponse: 异常");
            return false;
        }
        OutputStream outputStream = null;
        try {
            outputStream = contentResolver.openOutputStream(insert);//写入图片的流
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            byte[] bytes = new byte[1024*2];
            int len;
            while ((len=inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
            }
            outputStream.flush();
        }catch (IOException e){
            Log.e(TAG, "saveImage: "+e );
        }finally {
            try {
                outputStream.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }
 
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
 
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
 
}

OkHttpUtils.class

package com.xunua.networkproject.Utils;

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * @user XUNUA
 * @email xunua@outlook.com
 * @creat time 2020/4/26 1:32
 *
 *         post请求参照此:https://blog.csdn.net/w605283073/article/details/103797118?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522158784284619725222431143%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=158784284619725222431143&biz_id=0&utm_source=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v25-1
 *
 *     //okhttp3
 *     implementation("com.squareup.okhttp3:okhttp:4.5.0")
 *
 */
public class OkHttpUtils  {
    /**
     *  get请求(常用)
     * @param url       url参数是链接地址
     * @return
     * @throws IOException
     */
    public String get(String url) throws IOException {
        OkHttpClient client = new OkHttpClient.Builder()
                .callTimeout(100000, TimeUnit.MILLISECONDS)
                .build();
        Request request = new Request.Builder()
                .url(url)
                .build();
        try (Response response = client.newCall(request).execute()) {
            return response.body().string();
        }
    }


    /**
     * Post请求(常用)       这个是key value形式
     * 标准请求,将post请求数据写在hashmap中 进行遍历
     * @param url               url参数是链接地址
     * @param hashMap           这个是post请求的key value        hashmap.put("请求key1","value1")  hashmap.put("请求key2","value2")
     * @return
     * @throws IOException
     */
    public String post(String url, HashMap<String,String> hashMap) throws IOException {
        OkHttpClient client = new OkHttpClient.Builder()
                .callTimeout(100000, TimeUnit.MILLISECONDS)
                .build();
        FormBody.Builder builder = new FormBody.Builder();
        for (String key:
                hashMap.keySet()) {
            builder.add(key, hashMap.get(key));
        }
        RequestBody body = builder
                .build();
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        try (Response response = client.newCall(request).execute()) {
            return response.body().string();
        }
    }

    /**
     * post请求    这个是json数据形式
     *
     *这个post请求是发送json数据来进行请求   与上面的不一样    (这个不常用)  只是官网有这个方法,就留在这里
     */
    public static final MediaType JSON
            = MediaType.get("application/json; charset=utf-8");
    String post(String url, String json) throws IOException {
        OkHttpClient client = new OkHttpClient.Builder()
                .callTimeout(100000, TimeUnit.MILLISECONDS)
                .build();
        RequestBody body = RequestBody.create(json, JSON);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        try (Response response = client.newCall(request).execute()) {
            return response.body().string();
        }
    }

    /**
     *     OkHttp上传单个文件
     * @param uri   接口地址
     * @param context   上下文
     * @param fileUri   文件的uri地址 不再是path路径了  分区存储后只能先得到文件的uri
     * @return      返回response  记得判断返回值是否为null
     */
    public static Response upload(String uri, Context context,Uri fileUri){
        InputStream inputStream=null;
        Response response=null;
        try {
            //client
            OkHttpClient client = new OkHttpClient.Builder()
                    .connectTimeout(10000, TimeUnit.MILLISECONDS)
                    .build();

            String path = FileUtils.getPath(context, fileUri);//通过uri得到了文件的绝对路径
            File file = new File(path);//特定新建一个file类
            String fileName = file.getName();//来获取文件名  主要是文件名的后缀
            inputStream = context.getContentResolver().openInputStream(fileUri);

            byte[] bytes = FileUtils.toByteArray(inputStream);

            String type = context.getContentResolver().getType(fileUri);
            RequestBody requestBody = RequestBody.create(bytes, MediaType.parse(type));
            MultipartBody multipartBody = new MultipartBody.Builder()
                    .addFormDataPart("file",fileName, requestBody)
                    .build();
            Request request = new Request.Builder()
                    .url(String.valueOf(uri))
                    .post(multipartBody)
                    .build();
            response = client.newCall(request).execute();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return response;
    }

    /**
     * OkHttp上传多个文件      支持上传视频
     * @param uri       接口的uri地址
     * @param fileUris     存放上传文件的uri地址的集合
     * @param context       上下文
     */
    void uploads(String uri,List<Uri> fileUris,Context context){
        InputStream inputStream=null;
        try {
            //client
            OkHttpClient client = new OkHttpClient.Builder()
                    .connectTimeout(10000, TimeUnit.MILLISECONDS)
                    .build();
            MultipartBody.Builder mulBuilder = new MultipartBody.Builder();
            for (Uri fileUri:
                    fileUris) {
                String path = FileUtils.getPath(context,fileUri);
                File file = new File(path);
                String fileName = file.getName();
                inputStream=context.getContentResolver().openInputStream(fileUri);
                byte[] bytes = FileUtils.toByteArray(inputStream);
                RequestBody requestBody = RequestBody.create(bytes, MediaType.parse(context.getContentResolver().getType(fileUri)));
                mulBuilder.addFormDataPart("files",fileName,requestBody);
            }
            MultipartBody multipartBody = mulBuilder.build();
            Request request = new Request.Builder()
                    .url(uri)
                    .post(multipartBody)
                    .build();
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull IOException e) {

                }
                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {

                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

另附上我的git仓库地址,内有okhttp来发送get请求post请求 文件上传 多个文件上传 文件下载的实例:
https://gitee.com/linxunyou/NetWorkProject

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读