①:android10以下检查文件,通常会调用if (file.exists())来判断,android10失效,需要通过查询Uri来获取:
@RequiresApi(Build.VERSION_CODES.Q)
fun getDownloadUri(filename:String,contentResolver:ContentResolver) :Uri?{
//根据传入的文件名参数查询。注意这里的MediaStore.Downloads,要和存入时的相对路径一样。
val cursor=contentResolver.query(
MediaStore.Downloads.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Files.FileColumns._ID),
MediaStore.Downloads.DISPLAY_NAME + "=?",
arrayOf(filename),
null
)
if (cursor != null && cursor.moveToFirst()) {
//查出id
val id: Int = cursor.getInt(cursor.getColumnIndex(MediaStore.Downloads._ID))
//根据id查询URI
return ContentUris.withAppendedId(
MediaStore.Downloads.EXTERNAL_CONTENT_URI,
id.toLong()
)
}
//关闭查询
//关闭查询
cursor?.close()
return null
}
uri如果为空,表明文件不存在,注意:android10 存储文件时文件名如果重复,会自动变成aa.png(1),aa.png(2),aa.png(3)等这种形式,超过一定数量,查询文件同样失败。
接着根据查询到的uri来获取绝对路径,代码如下:
var path=PathUtils.getPath(this,uri)
工具类:
public class PathUtils {
public static final String DOC = "application/msword";
public static final String DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
public static final String XLS = "application/vnd.ms-excel application/x-excel";
public static final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
public static final String PPT = "application/vnd.ms-powerpoint";
public static final String PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
public static final String PDF = "application/pdf";
/**
* 文件Uri转路径(兼容各品牌手机)
*
* val i = Intent(
* Intent.ACTION_GET_CONTENT
* ).apply {
* val mimeTypes = arrayOf<String>(
* PathUtils.DOC,
* PathUtils.DOCX,
* PathUtils.PDF,
* PathUtils.PPT,
* PathUtils.PPTX,
* PathUtils.XLS,
* PathUtils.XLSX
* )
* setType("application/*");
* addCategory(Intent.CATEGORY_OPENABLE);
* putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
* startActivityForResult(this, GALLERY)
* }
*/
/**
* android7.0以上处理方法
*/
private static String getFilePathForN(Context context, Uri uri) {
try {
Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
File file = new File(context.getFilesDir(), name);
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
returnCursor.close();
inputStream.close();
outputStream.close();
return file.getPath();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 全平台处理方法
*/
public static String getPath(final Context context, final Uri uri) throws Exception {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
final boolean isN = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
if (isN) {
return getFilePathForN(context, uri);
}
// 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);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
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];
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;
}
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())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* 获取此Uri的数据列的值。这对于MediaStore uri和其他基于文件的内容提供程序非常有用。
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} catch (IllegalArgumentException e) {
//do nothing
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
}
android10存储时的文件路径:
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) //类似的相对路径。
android10以上写入文件:
val bis = BufferedInputStream(inputStream)
val values = ContentValues()
//这里的file.name,和MediaStore.Downloads,需要和之前的查询uri方法对应。
values.put(MediaStore.Downloads.DISPLAY_NAME, file.name)
values.put(MediaStore.Downloads.RELATIVE_PATH,Environment.DIRECTORY_DOWNLOADS)
val uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI,values)
if (uri != null) {
val outputStream = contentResolver.openOutputStream(uri)
if (outputStream != null) {
val bos = BufferedOutputStream(outputStream)
val buffer = ByteArray(1024)
var bytes = bis.read(buffer)
while (bytes >= 0) {
bos.write(buffer, 0, bytes)
bos.flush()
bytes = bis.read(buffer)
}
bos.close()
}
} else {
progressListener.failed("下载失败")
}
bis.close()
文件写入完成后,需要在手机文件夹“最近文件”里显示,需要刷新:
MediaScannerConnection.scanFile(
context,
arrayOf<String>(file.absolutePath),
null
) { path, uri ->
}
②以前在文件下载后,需要预览,使用腾讯的Tbs服务,android10以下:旧版的tbs正常使用,新版貌似去掉该功能;
android10以上:Tabs文件服务无法使用,可以使用微软的工具:
//webview打开如下链接,后面是文件在线地址。
"http://view.officeapps.live.com/op/view.aspx?src="+fileUrl
或者使用手机自带第三方软件查看文件:
fun Activity.openFileScanViewer(file: File) {
var uri: Uri?=null
if (isAndroid7Above()) {
uri = FileProvider.getUriForFile(this, packageName, file)
}else{
uri = Uri.fromFile(file)
}
val intent =
Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent)
}