Ожидаемое поведение
Когда я выбираю файл, который хранится внутри «Загрузки», он должен получить возможность получить имя и путь к файлу.
Фактическое поведение
Когда я выбираю файл, который хранится внутри «Download», он возвращает null.
Шаги по воспроизведению проблемы
Here is the code what i implemented
public static String getPath(final Context context, final Uri uri) {
String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
try {
final boolean isOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
String stringContentURI;
Uri contentUri;
if (isOreo){
stringContentURI = "content://downloads/my_downloads";
}else{
stringContentURI = "content://downloads/public_downloads";
}
contentUri = ContentUris.withAppendedId(
Uri.parse(stringContentURI), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
return null;
}
}
}
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 index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
Однако он работает при выборе файла из других папок на устройстве Android.
Пожалуйста, порекомендуйте. Спасибо всем :)
На данный момент лучший способ получить путь:
Получение физического файла из URI как InputStream,
ContentResolver.openInputStream() позволяет вам получить доступ к содержимому файла, не зная его реального пути
String id = DocumentsContract.getDocumentId(uri);
InputStream inputStream = getContentResolver().openInputStream(uri);
затем запишите его как временный файл в кешированное хранилище
File file = new File(getCacheDir().getAbsolutePath()+"/"+id);
writeFile(inputStream, file);
String filePath = file.getAbsolutePath();
Here is the method to write temporary file into cached storage
void writeFile(InputStream in, File file) {
OutputStream out = null;
try {
out = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while((len=in.read(buf))>0){
out.write(buf,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}
finally {
try {
if ( out != null ) {
out.close();
}
in.close();
} catch ( IOException e ) {
e.printStackTrace();
}
}
}
Не уверен, что это лучший способ, но код работает правильно: D
Это спасительное решение. Спасибо. Но мне все еще интересно, почему ребята из Google не могут сделать это простым. Я думаю, что просто получить путь к файлу - это большая работа.
@ryosu просто оставил ответ, который менее сложен - и возвращает фактический путь.
@ryosu Он работает нормально до Android N, но в Android P он не работает при выборе изображений из папки «Загрузки» и сбоях. Можете ли вы предложить какой-либо другой способ.
Вот еще одно решение, которое я нашел из этой сути. FileUtils.java
Вот как им пользоваться.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Uri uri = data.getData();
File originalFile = new File(FileUtils.getRealPath(this,uri));
}
Я не знаю, зачем давать «ответ неполный» для этого ответа, но это полезно для меня, спасибо @ nimrod-mania
Не работает в Android 30
Это то, что я только что придумал (он протестирован на Android Pie и предполагает наличие SD-карты):
private String getParentDirectory(@NonNull Uri uri) {
String uriPath = uri.getPath();
String filePath = Environment.getExternalStorageDirectory().getAbsolutePath();
if (uriPath != null) {filePath = new File(filePath.concat("/" + uriPath.split(":")[1])).getParent();}
return filePath;
}
private String getAbsolutePath(@NonNull Uri uri) {
String uriPath = uri.getPath();
String filePath = Environment.getExternalStorageDirectory().getAbsolutePath();
if (uriPath != null) {filePath = filePath.concat("/" + uriPath.split(":")[1]);}
return filePath;
}
this is activity instance variable
*************************************************************************
*************************************************************************
Uri filePath;
String strAttachmentFileName = "",//attached file name
strAttachmentCoded = "";//attached file in byte code Base64
int PICK_REQUEST =1;
*************************************************************************
*************************************************************************
this is in activity method
*************************************************************************
*************************************************************************
Button buttonChoose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setType("file/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select any file"), PICK_REQUEST );
}
});
*************************************************************************
*************************************************************************
this is overrride activity method
*************************************************************************
*************************************************************************
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_REQUEST && resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
filePath = data.getData();
File uploadFile = new File(FileUtils.getRealPath(activity.this, filePath));
try {
if (uploadFile != null) {
strAttachmentFileName = uploadFile.getName();
FileInputStream objFileIS = new FileInputStream(uploadFile);
ByteArrayOutputStream objByteArrayOS = new ByteArrayOutputStream();
byte[] byteBufferString = new byte[1024];
int readNum;
readNum = objFileIS.read(byteBufferString);
while (readNum != -1) {
objByteArrayOS.write(byteBufferString, 0, readNum);
readNum = objFileIS.read(byteBufferString);
}
strAttachmentCoded = Base64.encodeToString(objByteArrayOS.toByteArray(), Base64.DEFAULT);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
*************************************************************************
*************************************************************************
Please create file FileUtils.java as below
byte[] p = Convert.FromBase64String("byte string");
MemoryStream ms = new MemoryStream(p);
FileStream fs = new FileStream
(System.Web.Hosting.HostingEnvironment.MapPath("~/ComplaintDetailsFile/") +
item.FileName, FileMode.Create);
ms.WriteTo(fs);
ms.Close();
fs.Close();
fs.Dispose();
идеальное решение
Ниже будет полезен API, нам нужно создать файл в кеше нашего приложения и вернуть тот же путь из-за ограничений, то есть Ограниченное хранилище.
fun createFileAndGetPathFromCacheFolder(context: Context, uri: Uri): String? {
var inputStream: FileInputStream? = null
var outputStream: FileOutputStream? = null
try {
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, "r", null)
inputStream = FileInputStream(parcelFileDescriptor?.fileDescriptor)
val file = File(context.cacheDir, context.contentResolver.getFileName(uri))
outputStream = FileOutputStream(file)
val buffer = ByteArray(1024)
var len: Int
while (inputStream.read(buffer).also { len = it } != -1) {
outputStream.write(buffer, 0, len)
}
return file.absolutePath
} catch (e: Exception) {
} finally {
inputStream?.close()
outputStream?.close()
}
return " "
}
Но он по-прежнему не дает вам реального пути к файлу.