Осуществление выбора файлов в Android и копирование выбранного файла в другое место
я пытаюсь реализовать средство выбора файлов в своем проекте 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
, etc... И я не нахожу способ сделать это правильно, я попытался получить Path
затем получить File
этой Path
... но это не работает, поэтому я думаю, что лучше получить , а потом с этим File
программно я Copy
этот и 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 ответ, но я не знаю, смогу ли с ним Copy
этой File
в другую часть моего SDcard
я waitting для его ответ.
@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()
С @С. Ю.
я делаю так :
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.ленг.RuntimeException: сбой доставки результата ResultInfo{who=null, request=131073, result=-1, data=Intent { dat=file:///path typ=text/plain flg=0x3 }} к действию {info.androidhive.tabsswipe / info.androidhive.tabsswipe.MainActivity2}: java.ленг.Исключение NullPointerException
редактировать с помощью кода работа благодаря @Ю. С., @Lukas Knuth и @CommonsWare.
это 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();
(это было потрясающе @Y. S. Не знал об этой функции), и я создаю второй 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","....");
}
}
еще раз спасибо за вашу помощь, надеюсь, вам понравится этот код, сделанный со всеми вами, ребята:)
5 ответов
Шаг 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);
}
At хотя бы один из этих двух методов должен дать вам правильный, полный путь.
Шаг 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();
}
}
попробуйте это. Это должно сработать.
Примечание: Vis-a-vis Lukas' ответ - то, что он сделал, это использовать метод под названием openInputStream()
возвращает контент of a 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's 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
.
Я сделал то же самое, чтобы позволить пользователю выбрать изображение из папки :
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 drive,...:
открытый класс 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());
}
}
заключение: код работает с путем изображения, но уверен, работает с любым типом файла.
надеюсь, что это поможет решить вашу проблему.
мирный.
As @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();
}
}
удаление файла, вероятно, невозможно, так как файл не принадлежат для вас он принадлежит приложению, которое поделилось им с вашим. Таким образом, приложение-владелец отвечает за удаление файла.
A Uri
не является файлом. А Uri
ближе к URL веб-сервера. Это непрозрачный адрес, который имеет значение только для " сервера "(или в этом случае ContentProvider
).
так же, как вы используете InputStream
для чтения в байтах, представленных веб-URL, вы используете InputStream
для чтения в байтах, представленных Uri
. Вы получаете такой поток, позвонив openInputStream()
на ContentResolver
.
передайте 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;
}