Task: I want to copy selected files from A folder to B folder. Both folders are in the external storage.
Problem: It works perfectly fine, however, at some point it just stops copying files. For example, if I want to copy 500 files, it would copy only 110 files. Also I've noticed that I can't copy video files, it works only with images.
Code:
The method which I use to copy files:
private static void makeFileCopy(File source, File dest) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(source);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
}finally {
try {
if (is != null)
is.close();
if (os != null)
os.close();
}catch (IOException ex) {
ex.printStackTrace();
}
}
}
One more:
public static void copyFileList(Context context, List<MediaFile> contentList, File mediaFolder) {
if (contentList != null) {
ContentValues values=new ContentValues();
for (int index=0;index<contentList.size();index++) {
MediaFile mediaFile=contentList.get(index);
File file = new File(mediaFolder, mediaFile.mediaFile().getName());
boolean isVideo=mediaFile.getType()== MediaFile.Type.VIDEO;
if (!file.exists()) {
try {
if (!file.createNewFile()) {
continue;
}
FileUtils.makeFileCopy(mediaFile.getRealFile().getAbsoluteFile(), file);
} catch (IOException ex) {
ex.printStackTrace();
continue;
}
if (isVideo) {
values.put(MediaStore.Video.VideoColumns.DATA, file.getAbsolutePath());
context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
} else {
values.put(MediaStore.Images.ImageColumns.DATA, file.getAbsolutePath());
context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
values.clear();
}
}
}
}
Thank you!
Finally, I've solved that problem. This is extremely stupid mistake I've made.
Solution: I wanted to make a copy of files which I already had in my destination folder, and by checking if (!file.exists()) it just did not pass. So, I've come up with the following code:
public static void copyFileList(Context context, List<MediaFile> contentList, File mediaFolder) {
if (contentList != null) {
ContentValues values=new ContentValues();
for (int index=0;index<contentList.size();index++) {
MediaFile mediaFile=contentList.get(index);
String fileName=mediaFile.mediaFile().getName();
boolean isVideo=mediaFile.getType()== MediaFile.Type.VIDEO;
File file = new File(mediaFolder, fileName);
//let a user to decide whether to create a copy of already existing files
if(!file.exists()) {
file=new File(mediaFolder,uniqueNameFor(fileName));
}
if(!file.exists()) {
try {
FileUtils.makeFileCopy(mediaFile.mediaFile().getAbsoluteFile(), file);
} catch (IOException ex) {
ex.printStackTrace();
continue;
}
if (isVideo) {
values.put(MediaStore.Video.VideoColumns.DATA, file.getAbsolutePath());
values.put(MediaStore.Video.VideoColumns.MIME_TYPE,mediaFile.getMimeType());
context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
} else {
values.put(MediaStore.Images.ImageColumns.DATA, file.getAbsolutePath());
values.put(MediaStore.Images.ImageColumns.MIME_TYPE,mediaFile.getMimeType());
context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
}
values.clear();
}
}
}
Just create a unique name of the file.
Also I did change the copy method:
private static void makeFileCopy(File source, File dest) throws IOException {
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
inputChannel = new FileInputStream(source).getChannel();
outputChannel = new FileOutputStream(dest).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} finally {
try {
if (inputChannel != null)
inputChannel.close();
if (outputChannel != null)
outputChannel.close();
}catch (IOException ex) {
ex.printStackTrace();
}
}
}
Using channels is a little bit faster than using previous approach.
Check out 4 ways how to copy files here.
Thank you for help!
Related
I have successfully downloaded the file from the server and saved it on Android storage, but the file is stored in a deep path, for example: /storage/emulated/0/Android/data/example.com.yourapp/files/Download/example_file.pdf
The code I'm currently using is like this:
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
File filesDir = getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
assert filesDir != null;
downloadedFile = new File(filesDir,
"file_name" + ".pdf");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(downloadedFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
}
outputStream.flush();
Functions.scanFile(getContext(), downloadedFile, "application/pdf");
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
What I want, when the download process is successful and I open the file manager, the latest file that already downloaded can be seen in the latest file before.
I don't know yet, but I think I need to make use of MediaStore to make my files visible in File Manager by trying this code:
public static void scanFile(Context ctxt, File f, String mimeType) {
MediaScannerConnection.scanFile(ctxt, new String[] {f.getAbsolutePath()}, new String[] {mimeType}, null);
}
But it didn't work either. How do i achieve this?
So i just change File filesDir = getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
into File filesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
And it's available in recent files in File manager
My Zipped folder contains sub-folder with files but while extracting it, I am not able to achieve the same hierarchy. I'm getting the unzipped structure as follows:-
/storage/emulated/0/unzipped_folder/sub_folder\main.png
/storage/emulated/0/unzipped_folder/sub_folder\test.xml
So while extracting it, I'm not able to get sub_folder as a directory.
I'm using below code while extracting the zip file.
public static void unzip(String zipFile, String location) throws IOException {
try {
File f = new File(location);
if (!f.isDirectory()) {
f.mkdirs();
}
ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile));
try {
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
String path = location + File.separator + ze.getName();
if (ze.isDirectory()) {
File unzipFile = new File(path);
if (!unzipFile.isDirectory()) {
unzipFile.mkdirs();
}
} else {
FileOutputStream fout = new FileOutputStream(path, false);
try {
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
} finally {
fout.close();
}
}
}
} finally {
zin.close();
}
} catch (Exception e) {
e.printStackTrace();
Log.e("ZIP STU", "Unzip exception", e);
}
}
Please help, I'm stuck in this for more than 2 days.
Thanks!
Finally I'm able to solve this issue using below code.
public static void unzipEPub(File zipFile, File destinationDir){
ZipFile zip = null;
try {
int DEFUALT_BUFFER = 1024;
destinationDir.mkdirs();
zip = new ZipFile(zipFile);
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
while (zipFileEntries.hasMoreElements()) {
ZipEntry entry = zipFileEntries.nextElement();
String entryName = entry.getName();
entryName = entryName.replace("\\","/");
File destFile = new File(destinationDir, entryName);
File destinationParent = destFile.getParentFile();
if (destinationParent != null && !destinationParent.exists()) {
destinationParent.mkdirs();
}
if (!entry.isDirectory()) {
BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
int currentByte;
byte data[] = new byte[DEFUALT_BUFFER];
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos, DEFUALT_BUFFER);
while ((currentByte = is.read(data, 0, DEFUALT_BUFFER)) > 0) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (zip != null) {
try {
zip.close();
} catch (IOException ignored) {
}
}
}
}
There is an image file inside a directory. How to copy this image file into another directory that was just created ? The two directories are on the same internal storage of the device :)
You can use these functions. The first one will copy whole directory with all children or a single file if you pass in a file. The second one is only usefull for files and is called for each file in the first one.
Also note you need to have permissions to do that
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Functions:
public static void copyFileOrDirectory(String srcDir, String dstDir) {
try {
File src = new File(srcDir);
File dst = new File(dstDir, src.getName());
if (src.isDirectory()) {
String files[] = src.list();
int filesLength = files.length;
for (int i = 0; i < filesLength; i++) {
String src1 = (new File(src, files[i]).getPath());
String dst1 = dst.getPath();
copyFileOrDirectory(src1, dst1);
}
} else {
copyFile(src, dst);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copyFile(File sourceFile, File destFile) throws IOException {
if (!destFile.getParentFile().exists())
destFile.getParentFile().mkdirs();
if (!destFile.exists()) {
destFile.createNewFile();
}
FileChannel source = null;
FileChannel destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
destination.transferFrom(source, 0, source.size());
} finally {
if (source != null) {
source.close();
}
if (destination != null) {
destination.close();
}
}
}
If you want to copy image programtically then use following code.
File sourceLocation= new File (sourcepath);
File targetLocation= new File (targetpath);
InputStream in = new FileInputStream(sourceLocation);
OutputStream out = new FileOutputStream(targetLocation);
// Copy the bits from instream to outstream
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
** Use FileUtils This Is Simple Fast And Best method and Download Jar file from here**
public void MoveFiles(String sourcepath) {
File source_f = new File(sourcepath);
String destinationPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/WhatsappStatus/yourfilename.mp4";
File destination = new File(destinationPath);
try
{
FileUtils.copyFile(source_f , destination);
}
catch (IOException e)
{
e.printStackTrace();
}
}
Go To Link For FileUtils Jar
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();
}
}
}
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;
}
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();
}
}
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();
}
}
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.
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)
}
}
}
}
You can do it using
new File(uri.getPath());
When you use emulator your sqlite file is stored in a folder near your main application folder and you can download it. But this feature is not accessible in not rooted devices. How can I backup this existing sqlite file in SD Card programmatically?
I want to have a button in my application that stores this file in a special path in my SD Card. Is it possible?
Thanks,
You can try this, work for me, remember to get the WRITE_EXTERNAL_STORAGE permission in your manifest:
// Copy to sdcard for debug use
public static void copyDatabase(Context c, String DATABASE_NAME) {
String databasePath = c.getDatabasePath(DATABASE_NAME).getPath();
File f = new File(databasePath);
OutputStream myOutput = null;
InputStream myInput = null;
Log.d("testing", " testing db path " + databasePath);
Log.d("testing", " testing db exist " + f.exists());
if (f.exists()) {
try {
File directory = new File("/mnt/sdcard/DB_DEBUG");
if (!directory.exists())
directory.mkdir();
myOutput = new FileOutputStream(directory.getAbsolutePath()
+ "/" + DATABASE_NAME);
myInput = new FileInputStream(databasePath);
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.flush();
} catch (Exception e) {
} finally {
try {
if (myOutput != null) {
myOutput.close();
myOutput = null;
}
if (myInput != null) {
myInput.close();
myInput = null;
}
} catch (Exception e) {
}
}
}
}
You can try following code,
String path = Environment.getExternalStorageDirectory().toString() + "/path";
File folder = new File( path );
if (!folder.exists())
{
folder.mkdirs();
}
File dbfile = new File( path + "/database.db" );
if ( !dbfile.exists() )
{
dbFile.createFile();
}
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
You can try this to copy a file:
public void copyFile(File in, File out) {
String DialogTitel = getString(R.string.daten_wait_titel);
String DialogText = getString(R.string.kopiervorgang_laeuft);
try {
// Dialogdefinition Prograssbar
final ProgressDialog dialog = new ProgressDialog(this) {
#Override
public boolean onSearchRequested() {
return false;
}
};
dialog.setCancelable(false);
dialog.setTitle(DialogTitel);
dialog.setIcon(R.drawable.icon);
dialog.setMessage(DialogText);
dialog.show();
new Thread(new MyCopyThread(in, out)) {
#Override
public void run() {
try {
FileChannel inChannel = new FileInputStream(
MyCopyThread.in).getChannel();
FileChannel outChannel = new FileOutputStream(
MyCopyThread.out).getChannel();
try {
System.out.println("KOPIEREN");
inChannel.transferTo(0, inChannel.size(),
outChannel);
if (inChannel != null)
inChannel.close();
if (outChannel != null)
outChannel.close();
setCopyError(false);
} catch (IOException e) {
setCopyError(true);
// throw e;
} finally {
if (inChannel != null)
inChannel.close();
if (outChannel != null)
outChannel.close();
}
dialog.dismiss();
// Abschlussarbeiten
if (useExternalSD == true) {
// Externe DB
moveDBtoExternFinish();
} else {
// Interne DB
moveDBtoInternFinish();
}
moveDBFinishHandler.sendMessage(moveDBFinishHandler
.obtainMessage());
} catch (Exception ex) {
}
}
}.start();
} catch (Exception exx) {
}
}
This is the code to get the filname of your internal db:
File interneDB = getApplicationContext().getDatabasePath(MY_DB_NAME);
Replace MY_DB_NAME with the name of your DB