Реализация File Picker в Android и копирование выбранного файла в другое место
Я пытаюсь реализовать File Picker в своем проекте Android. Что мне удалось сделать до сих пор:
Intent chooseFile;
Intent intent;
chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
intent = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(intent, PICKFILE_RESULT_CODE);
И затем в моем onActivityResult()
switch(requestCode){
case PICKFILE_RESULT_CODE:
if(resultCode==-1){
Uri uri = data.getData();
String filePath = uri.getPath();
Toast.makeText(getActivity(), filePath,
Toast.LENGTH_LONG).show();
}
break;
}
Это открывает сборщик файлов, но это не то, что я хочу. Например, я хочу выбрать файл (.txt), а затем получить этот File
, а затем использовать его. С помощью этого кода я думал, что получаю полный путь, но этого не произойдет; например, я получаю: /document/5318/
. Но с этим путем я не могу получить файл. Я создал метод под названием PathToFile()
, который возвращает File
:
private File PathToFile(String path) {
File tempFileToUpload;
tempFileToUpload = new File(path);
return tempFileToUpload;
}
То, что я пытаюсь сделать, это позволить пользователю выбрать File
из любых средств DropBox
, Drive
, SDCard
, Mega
и т.д.... И я не нахожу способ чтобы сделать это правильно, я попытался получить Path
, затем получить File
этим Path
... но он не работает, поэтому я думаю, что лучше получить сам File
, а затем с помощью это File
программно я Copy
this или Delete
.
EDIT (текущий код)
Мой Intent
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);
У меня вопрос, потому что я не знаю, что поддерживается как text/plain
, но я собираюсь рассказать об этом, но в данный момент это не имеет значения.
На моем onActivityResult()
я использовал то же самое, что @Lukas Knuth answer, но я не знаю, могу ли я с ним Copy
this File
в другую часть от моего SDCard
Я жду ответа.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
//get the path
Log.d("Path???", content_describer.getPath());
BufferedReader reader = null;
try {
// open the user-picked file for reading:
InputStream in = getActivity().getContentResolver().openInputStream(content_describer);
// now read the content:
reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null){
builder.append(line);
}
// Do something with the content in
text.setText(builder.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
getPath()
из @Y.S.
Я делаю это:
String[] projection = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = getActivity().getContentResolver().query(content_describer, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
cursor.close();
Log.d( "PATH-->",cursor.getString(column_index));
Получает NullPointerException
:
java.lang.RuntimeException: Ошибка предоставления результата ResultInfo {who = null, request = 131073, result = -1, data = Intent {dat = file:///path typ = text/plain flg = 0x3}} to activity {info.androidhive.tabsswipe/info.androidhive.tabsswipe.MainActivity2}: java.lang.NullPointerException
Это Intent
, где я принимаю только файлы text/plain
.
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);
На моем onActivityResult()
я создаю URI
, где я получаю данные Intent
, я создаю File
, где я сохраняю абсолютный путь content_describer.getPath();
, и то я сохраняю имя пути, чтобы использовать его в TextView
с content_describer.getLastPathSegment();
(это было удивительно, что @YS не знал об этой функции), и я создаю второй File
, который я назвал destination
и Я отправляю AbsolutePath
, чтобы создать этот File
.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
String src = content_describer.getPath();
source = new File(src);
Log.d("src is ", source.toString());
String filename = content_describer.getLastPathSegment();
text.setText(filename);
Log.d("FileName is ",filename);
destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Test/TestTest/" + filename);
Log.d("Destination is ", destination.toString());
SetToFolder.setEnabled(true);
}
}
Также я создал функцию, которую вы должны отправить source file
и destination file
, которые мы создали ранее, чтобы скопировать ее в новую папку.
private void copy(File source, File destination) throws IOException {
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(destination).getChannel();
try {
in.transferTo(0, in.size(), out);
} catch(Exception e){
Log.d("Exception", e.toString());
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
Также я создал функцию, которая говорит мне, если эта папка существует или нет (мне нужно отправить destination file
, если она не существует, я создаю эту папку, а если нет, я ничего не делаю.
private void DirectoryExist (File destination) {
if(!destination.isDirectory()) {
if(destination.mkdirs()){
Log.d("Carpeta creada","....");
}else{
Log.d("Carpeta no creada","....");
}
}
Еще раз спасибо за вашу помощь, надеюсь, вам понравится этот код, сделанный с каждым из вас, ребята:)
Ответы
Ответ 1
ШАГ 1 - Используйте неявный Intent
:
Чтобы выбрать файл с устройства, вы должны использовать неявный Intent
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
chooseFile = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(chooseFile, PICKFILE_RESULT_CODE);
ШАГ 2 - Получить абсолютный путь к файлу:
Чтобы получить путь к файлу из Uri
, сначала попробуйте использовать
Uri uri = data.getData();
String src = uri.getPath();
где data
- это Intent
, возвращенный в onActivityResult()
.
Если это не работает, используйте следующий метод:
public String getPath(Uri uri) {
String path = null;
String[] projection = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if(cursor == null){
path = uri.getPath()
}
else{
cursor.moveToFirst();
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
path = cursor.getString(column_index);
cursor.close();
}
return ((path == null || path.isEmpty()) ? (uri.getPath()) : path);
}
По крайней мере один из этих двух методов должен дать вам правильный полный путь.
ШАГ 3 - Скопируйте файл:
Я считаю, что вы хотите скопировать файл из одного места в другое.
Для этого необходимо указать абсолютный путь к файлу как исходного, так и конечного местоположений.
Сначала получите абсолютный путь к файлу, используя метод getPath()
или uri.getPath()
:
String src = getPath(uri); /* Method defined above. */
или
Uri uri = data.getData();
String src = uri.getPath();
Затем создайте два объекта File
следующим образом:
File source = new File(src);
String filename = uri.getLastPathSegment();
File destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/CustomFolder/" + filename);
где CustomFolder
- это каталог на внешнем диске, куда вы хотите скопировать файл.
Затем используйте следующий метод для копирования файла из одного места в другое:
private void copy(File source, File destination) {
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(destination).getChannel();
try {
in.transferTo(0, in.size(), out);
} catch(Exception){
// post to log
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
Попробуй это. Это должно сработать.
Примечание: в ответ на ответ лукаса - он использовал метод openInputStream()
, который возвращает содержимое Uri
, независимо от того, представляет ли Uri
файл или URL.
Еще один многообещающий подход - FileProvider
:
Есть еще один способ, с помощью которого можно получить файл из другого приложения. Если приложение обменивается файлами через FileProvider
, то можно получить объект FileDescriptor
, который содержит конкретную информацию об этом файле.
Для этого используйте следующее Intent
:
Intent mRequestFileIntent = new Intent(Intent.ACTION_GET_CONTENT);
mRequestFileIntent.setType("*/*");
startActivityForResult(mRequestFileIntent, 0);
и в вашем onActivityResult()
:
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent returnIntent) {
// If the selection didn't work
if (resultCode != RESULT_OK) {
// Exit without doing anything else
return;
} else {
// Get the file content URI from the incoming Intent
Uri returnUri = returnIntent.getData();
/*
* Try to open the file for "read" access using the
* returned URI. If the file isn't found, write to the
* error log and return.
*/
try {
/*
* Get the content resolver instance for this context, and use it
* to get a ParcelFileDescriptor for the file.
*/
mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e("MainActivity", "File not found.");
return;
}
// Get a regular file descriptor for the file
FileDescriptor fd = mInputPFD.getFileDescriptor();
...
}
}
где mInputPFD
- это ParcelFileDescriptor
.
Ссылки:
1. Общиесведения - Хранилище файлов.
2. FileChannel
.
3. FileProvider
.
4. Запрособщего файла.
Ответ 2
Я сделал то же самое, чтобы позволить пользователю выбрать изображение из папки:
1) есть кнопка ОТКРЫТЬ:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_open:
myOpenImagePicker();
break;
}
}
2) функция папки с открытыми изображениями:
@SuppressLint("InlinedApi")
public void myOpenImagePicker() {
if (Build.VERSION.SDK_INT < 19) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Select Picture"),
SELECT_FOLDER);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, SELECT_FOLDER);
}
}
3) результат действия, в котором я получаю путь к файлу изображения и делаю все, что захочу, с путем изображения:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case SELECT_FOLDER:
if (resultCode == RESULT_OK && data != null) {
Uri originalUri = data.getData();
String id01 = W_ImgFilePathUtil.getPath(
getApplicationContext(), originalUri);
Bitmap unscaledBitmap = W_ImgScalingUtil.decodeResource(id01,
xdrawing.getViewWidth(), xdrawing.getViewHeight(),
ScalingLogic.FIT);
if (unscaledBitmap == null) {
zprefsutil.ShowToast("IMAGE ERROR", 1);
} else {
setExternalScaledBitmap(W_ImgScalingUtil
.createScaledBitmap(unscaledBitmap,
xdrawing.getViewWidth(),
xdrawing.getViewHeight(), ScalingLogic.FIT));
unscaledBitmap.recycle();
xdrawing.invalidate();
}
}
break;
default:
break;
}
}
4) и теперь САМАЯ ВАЖНАЯ ЧАСТЬ, класс W_ImgFilePathUtil, код не от меня, но он позволяет вам получить полный путь к любому выбранному файлу, будь то на SD-карте, диске Google,...:
public class W_ImgFilePathUtil {
/**
* Method for return file path of Gallery image
*
* @param context
* @param uri
* @return path of the selected image file from gallery
*/
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) {
// check here to KITKAT or new version
final boolean isKitKatorUp = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKatorUp && 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.valueOf(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 the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
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;
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;
}
/**
* @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());
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri
.getAuthority());
}
}
ЗАКЛЮЧЕНИЕ: код работает с путем к изображению, но точно работает с любым типом файла.
Надеюсь, это поможет решить вашу проблему.
PEACE.
Ответ 3
Как уже отмечалось @CommonsWare, Android возвращает вам Uri
, более абстрактную концепцию, чем путь к файлу.
Он также может описывать простой путь к файлу, но он также может описывать ресурс, к которому осуществляется доступ через приложение (например, content://media/external/audio/media/710
).
Если вы хотите, чтобы ваш пользователь выбирал любой файл с телефона, чтобы прочитать его из вашего приложения, вы можете сделать это, запросив файл (как вы сделали правильно), а затем используйте ContentResolver
, чтобы получить InputStream
для Uri
, который возвращается сборщиком.
Вот пример:
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
// Ask specifically for something that can be opened:
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("*/*");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_REQUEST_CODE
);
// And then somewhere, in your activity:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_REQUEST_CODE && resultCode == RESULT_OK){
Uri content_describer = data.getData();
BufferedReader reader = null;
try {
// open the user-picked file for reading:
InputStream in = getContentResolver().openInputStream(content_describer);
// now read the content:
reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null){
builder.append(line);
}
// Do something with the content in
some_view.setText(builder.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Важно. Некоторые поставщики (например, Dropbox) хранят/кэшируют свои данные во внешнем хранилище. Вам нужно будет объявить android.permission.READ_EXTERNAL_STORAGE
-передачу в вашем манифесте, иначе вы получите FileNotFoundException
, даже если файл есть.
Обновить. Да, вы можете скопировать файл, читая его из одного потока и записывая его в другой:
// Error handling is omitted for shorter code!
Uri content_describer = data.getData();
InputStream in = null;
OutputStream out = null;
try {
// open the user-picked file for reading:
in = getContentResolver().openInputStream(content_describer);
// open the output-file:
out = new FileOutputStream(new File("some/path/to/a/writable/file"));
// copy the content:
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
// Contents are copied!
} finally {
if (in != null) {
in.close();
}
if (out != null){
out.close();
}
}
Удаление файла, вероятно, невозможно, так как файл не принадлежит вам, он принадлежит к приложению, которое поделилось им с вашим. Поэтому для удаления файла ответственное приложение несет ответственность.
Ответ 4
Передайте URI, возвращенный в onActivityResult в этом методе
private String getPath(Uri contentURI) {
String result;
Cursor cursor = getActivity().getContentResolver().query(contentURI,
null, null, null, null);
if (cursor == null) {
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor
.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
Ответ 5
A Uri
не является файлом. A Uri
ближе к URL-адресу веб-сервера. Это непрозрачный адрес, который имеет смысл только для "сервера" (или в этом случае ContentProvider
).
Так же, как вы используете InputStream
для чтения в байтах, представленных веб-URL, вы используете InputStream
для чтения в байтах, представленных Uri
. Вы получаете такой поток, вызывая openInputStream()
на ContentResolver
.
Ответ 6
Вот как реализовать средство выбора файлов и переместить выбранный файл в другое место (например, изображение).
Во-первых, добавьте средство выбора файлов с помощью кнопки прослушивания щелчка к своему коду следующим образом:
Кнопка выбора файла:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_choose_file:
showFileChooser();
break;
}
}
private String filePath = null;
private File sourceFile;
private static final int FILE_SELECT_CODE = 0;
private void showFileChooser() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(intent, "Select a File to Copy"),
FILE_SELECT_CODE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, "Please install a File Manager.",
Toast.LENGTH_SHORT).show();
}
}
Затем обработайте onActivityResult следующим образом:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case FILE_SELECT_CODE:
if (resultCode == RESULT_OK) {
// Get the Uri of the selected file
Uri uri = data.getData();
File file = new File(getCacheDir(), getFileName(uri));
int maxBufferSize = 1 * 1024 * 1024;
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
Log.e("InputStream Size","Size " + inputStream);
int bytesAvailable = inputStream.available();
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
Log.e("File Size","Size " + file.length());
inputStream.close();
outputStream.close();
file.getPath();
Log.e("File Path","Path " + file.getPath());
file.length();
Log.e("File Size","Size " + file.length());
if(file.length() > 0){
sourceFile = file;
saveFile(sourceFile);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
} else {
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
private void saveFile (File sourceFile) {
try {
File currentFile = sourceFile;
Bitmap myBitmap = BitmapFactory.decodeFile(currentFile.getAbsolutePath());
String path = currentFile.getAbsolutePath();
String idStr = path.substring(path.lastIndexOf('/') + 1);
File filepath = Environment.getExternalStorageDirectory();
File dir = new File(filepath.getAbsolutePath() + "/" + "yourFolderName" + "/");
if (!dir.exists()) {
dir.mkdirs();
}
String fileName = currentFile.getName();
file = new File(dir, fileName);
if (file.exists()) file.delete();
FileOutputStream fos = new FileOutputStream(file);
myBitmap.compress(CompressFormat.JPEG, 65, fos);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Примечание. Не забудьте добавить это разрешение в файл манифеста.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Надеюсь, это поможет.