以 Android 10 为目标平台,开启分区存储,不能直接通过文件路径(File)访问非应用专属存储空间的文件。因此,需要您通过 MediaStore 和 SAF 读取数据,并传给上传 SDK。详细说明请参考分区存储。
public interface BDMediaDataReader { /** * 打开对应 fileIndex 的文件 * * @param fileIndex 对应的文件 index,用于图片上传中多个文件上传。 * 例如第一个文件,fileIndex 为 0。 * @return 如果打开成功则返回 1,否则返回- 1. */ int open(int fileIndex); /** * 读取 size 大小的数据 * * @param fileIndex 对应的文件 index,用于图片上传中多个文件上传。 * @param offset 文件的 offset, * @param data 读取到的数据需要填到里面 * @param size 读取数据的大小 * @return 如果读到文件末尾返回 ReadFileEnd,读文件出错返回 ReadFileError。 */ int read(int fileIndex,long offset,byte[] data,int size); // 关闭对应 fileIndex 的文件 int close(int fileIndex); // 获取文件信息,例如 key == KeyIsGetFileSize 代表获取文件大小 long getValue(int fileIndex,int key); }
public class BDMediaDataReaderImpl implements BDMediaDataReader { ... } // 第二个参数为要上传的文件个数 mUploader.setMediaDataReader(new BDMediaDataReaderImpl(), 1);
使用 FileDescriptor 实现 BDMediaDataReader,其中需要注意如下信息:
以下代码示例实现了单个文件上传的功能,仅供参考。
package com.bytedance.vcloud.bduploader; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.system.Os; import android.system.OsConstants; import android.text.TextUtils; import android.util.Log; import com.ss.bduploader.BDMediaDataReader; import java.io.FileDescriptor; public class BDReaderImpl implements BDMediaDataReader { private static final String TAG = "ttmn BDReaderImpl"; private final Context mContext; private Uri mUri; private long mFileLength; private FileDescriptor mFd; private ParcelFileDescriptor mPfd; BDReaderImpl(Context context) { mContext = context; getVideoUri(context); } @Override public int open(int fileIndex) { Log.d(TAG, "open fileIndex:" + fileIndex); if (mContext == null || mUri == null) { return -1; } if (mFd != null) { return 1; } try { ContentResolver resolver = mContext.getApplicationContext().getContentResolver(); mPfd = resolver.openFileDescriptor(mUri, "r"); mFd = mPfd.getFileDescriptor(); mFileLength = Os.lseek(mFd, 0, OsConstants.SEEK_END); } catch (Exception e) { e.printStackTrace(); } return 1; } @Override public int read(int fileIndex, long offset, byte[] data, int size) { Log.d(TAG, "read fileIndex:" + fileIndex + ", offset:" + offset + ", dataSize:" + data.length + ", size:" + size); if (mFd == null) { return ReadFileError; } if (offset >= mFileLength) { return ReadFileEnd; } try { Os.lseek(mFd, offset, OsConstants.SEEK_SET); return Os.read(mFd, data, 0, size); } catch (Exception e) { e.printStackTrace(); return ReadFileError; } } @Override public int close(final int fileIndex) { if (mFd == null) { return 0; } try { Os.close(mFd); } catch (Throwable e) { e.printStackTrace(); } mFd = null; mPfd = null; return 0; } @Override public long getValue(int fileIndex, int key) { if (key == KeyIsGetFileSize) { return mFileLength; } return 0; } // android 10 分区存储,获取视频 Uri private void getVideoUri(Context context) { if (context == null) { return; } ContentResolver resolver = context.getApplicationContext().getContentResolver(); Uri collection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); } else { collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } String[] projection = new String[] { MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, }; String selection = MediaStore.Images.Media.MIME_TYPE + "=?"; String[] selectionArgs = { "video/mp4", }; String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC"; try (Cursor cursor = resolver.query(collection, projection, selection, selectionArgs, sortOrder )) { int idColumn = cursor.getColumnIndex(MediaStore.Video.Media._ID); int nameColumn = cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME); while (cursor.moveToNext()) { long id = cursor.getLong(idColumn); String name = cursor.getString(nameColumn); // 找到要上传的视频文件 if (TextUtils.equals(name, "testUpload.mp4")) { mUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id); } } } } }