How to save a media file (say .mp3) from an existing URI, which I am getting from an Implicit Intent?
Use this method, it works
void savefile(URI sourceuri)
{
String sourceFilename= sourceuri.getPath();
String destinationFilename = android.os.Environment.getExternalStorageDirectory().getPath()+File.separatorChar+"abc.mp3";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(sourceFilename));
bos = new BufferedOutputStream(new FileOutputStream(destinationFilename, false));
byte[] buf = new byte[1024];
bis.read(buf);
do {
bos.write(buf);
} while(bis.read(buf) != -1);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bis != null) bis.close();
if (bos != null) bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
sourceuri
looks like file:///something
and points to a file. This is no longer possible since API 24 (sharing file Uris between processes/apps is porhibited). Always use ContentResolver
to open content Uris. –
Trickish If Uri is received from Google Drive, it can be a Virtual File Uri too. Check this article from CommonsWare for more information. So you have to consider that condition too while saving file from Uri.
To find if file Uri is virtual or not you can use
private static boolean isVirtualFile(Context context, Uri uri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (!DocumentsContract.isDocumentUri(context, uri)) {
return false;
}
Cursor cursor = context.getContentResolver().query(
uri,
new String[]{DocumentsContract.Document.COLUMN_FLAGS},
null, null, null);
int flags = 0;
if (cursor.moveToFirst()) {
flags = cursor.getInt(0);
}
cursor.close();
return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
} else {
return false;
}
}
You can get the stream data from this virtual file like this:
private static InputStream getInputStreamForVirtualFile(Context context, Uri uri, String mimeTypeFilter)
throws IOException {
ContentResolver resolver = context.getContentResolver();
String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);
if (openableMimeTypes == null || openableMimeTypes.length < 1) {
throw new FileNotFoundException();
}
return resolver
.openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
.createInputStream();
}
For finding MIME type try
private static String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
Overall, you can use
public static boolean saveFile(Context context, String name, Uri sourceuri, String destinationDir, String destFileName) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
InputStream input = null;
boolean hasError = false;
try {
if (isVirtualFile(context, sourceuri)) {
input = getInputStreamForVirtualFile(context, sourceuri, getMimeType(name));
} else {
input = context.getContentResolver().openInputStream(sourceuri);
}
boolean directorySetupResult;
File destDir = new File(destinationDir);
if (!destDir.exists()) {
directorySetupResult = destDir.mkdirs();
} else if (!destDir.isDirectory()) {
directorySetupResult = replaceFileWithDir(destinationDir);
} else {
directorySetupResult = true;
}
if (!directorySetupResult) {
hasError = true;
} else {
String destination = destinationDir + File.separator + destFileName;
int originalsize = input.available();
bis = new BufferedInputStream(input);
bos = new BufferedOutputStream(new FileOutputStream(destination));
byte[] buf = new byte[originalsize];
bis.read(buf);
do {
bos.write(buf);
} while (bis.read(buf) != -1);
}
} catch (Exception e) {
e.printStackTrace();
hasError = true;
} finally {
try {
if (bos != null) {
bos.flush();
bos.close();
}
} catch (Exception ignored) {
}
}
return !hasError;
}
private static boolean replaceFileWithDir(String path) {
File file = new File(path);
if (!file.exists()) {
if (file.mkdirs()) {
return true;
}
} else if (file.delete()) {
File folder = new File(path);
if (folder.mkdirs()) {
return true;
}
}
return false;
}
Call this method from an AsycTask. Let me know if this helps.
private static String FILE_NAM = "video";
String outputfile = getFilesDir() + File.separator+FILE_NAM+"_tmp.mp4";
InputStream in = getContentResolver().openInputStream(videoFileUri);
private static File createFileFromInputStream(InputStream inputStream, String fileName) {
try {
File f = new File(fileName);
f.setWritable(true, false);
OutputStream outputStream = new FileOutputStream(f);
byte buffer[] = new byte[1024];
int length = 0;
while((length=inputStream.read(buffer)) > 0) {
outputStream.write(buffer,0,length);
}
outputStream.close();
inputStream.close();
return f;
} catch (IOException e) {
System.out.println("error in creating a file");
e.printStackTrace();
}
return null;
}
buffer[]
needed? Is that specific to saving a Uri
? –
Ribble I have used following code to save a file from an existing Uri given back from an Intent to an Uri that my App hosts:
private void copyFile(Uri pathFrom, Uri pathTo) throws IOException {
try (InputStream in = getContentResolver().openInputStream(pathFrom)) {
if(in == null) return;
try (OutputStream out = getContentResolver().openOutputStream(pathTo)) {
if(out == null) return;
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
}
}
Here's the easiest and the cleanest:
private void saveFile(Uri sourceUri, File destination)
try {
File source = new File(sourceUri.getPath());
FileChannel src = new FileInputStream(source).getChannel();
FileChannel dst = new FileOutputStream(destination).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
destination
be the entire File
path including name of file? Or just the directory name/path? –
Ribble File
denoting the entire file with its name. –
Dine java.io.FileNotFoundException: /document/image:18019: open failed: ENOENT (No such file or directory)
–
Dowse URI
returned by the Intent
opening gallery for choosing an image. At least it did not worked for me and probably someone who voted up my comment. I tested it with Android 5.1.1. –
Dowse When receiving a android.net.Uri
from an external source, the best way to save the file is from the stream:
try (InputStream ins = activity.getContentResolver().openInputStream(source_uri)) {
File dest = new File(destination_path);
createFileFromStream(ins, dest);
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
createFileFromStream
method:
public static void createFileFromStream(InputStream ins, File destination) {
try (OutputStream os = new FileOutputStream(destination)) {
byte[] buffer = new byte[4096];
int length;
while ((length = ins.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
}
I slightly modified Sebi's answer to work for Kotlin:
fun copyUri(context: Context, pathFrom: Uri, pathTo: Uri?) {
context.contentResolver.openInputStream(pathFrom).use { inputStream: InputStream? ->
if (pathTo == null || inputStream == null) return
context.contentResolver.openOutputStream(pathTo).use { out ->
if (out == null) return
// Transfer bytes from in to out
val buf = ByteArray(1024)
var len: Int
while (inputStream.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
}
}
}
1.Create a file from a URI path as:
File from = new File(uri.toString());
2.Create another File where you want the file to save as:
File to = new File("target file path");
3.Rename the file as:
from.renameTo(to);
With this the file from default path is automatically deleted and created at the new path.
How to get external storage location and save a file
This answer is not for the question, but for the title. It took hours to figure out how to do this since no article explains the process totally, while some of them are years old and uses deprecated APIs. Hope this might be helpful for future developers.
Get location of External Storage
For instance, from inside a fragment,
// when user choose file location
private val uriResult = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
// do when user choose file location
createFile(uri)
}
fun openFileChooser() {
// startActivityForResult() is deprecated
val suggestedFileName = "New Document.txt"
uriResult.launch(suggestedFileName)
}
Write file data using Uri
It may seem difficult to create a java.io.File
from an android.net.Uri
, since there is no direct way to convert an android.net.Uri
into java.net.URI
. But if you have the ApplicationContext
you can do it very easily.
fun createFile(uri: Uri) {
try {
requireContext().applicationContext.contentResolver.openFileDescriptor(uri, "w")?.use { fd ->
FileOutputStream(fd).use { fos ->
// do your job on the FileOutputStream
// also use background thread
fos.close()
}
}
} catch (e: Exception) {
}
}
Note: File operations throws multiple exceptions, so handle them carefully. And also do file operations in worker threads.
openOutputStream(destination)
instead of openFileDescriptor(uri, "w")
–
Tautologism You can do it using
new File(uri.getPath());
© 2022 - 2025 — McMap. All rights reserved.