I'm completely new to Android development and would like to index a file from the Android phone's external storage. I found some old code which used a deprecated method:
String file1 = Environment.getExternalStorageDirectory() + "/folder/file.blah";
I'm trying to rewrite this line and tried the Environment.getExternalFilesdir method but got a "cannot find symbol method" error. The only available method seems to be getExternalStorageState, but I can't figure out how to pass the file path argument in. How do I rewrite the deprecated code, using getExternalStorageState or otherwise? Thanks.
For getting directory of public file. you can use like below
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS);
docFile = new File(storageDir, docFileName);
Actually getExternalFilesDir() is not a part of Environment bundle. You can call it via the application context.
String file2 = getApplicationContext().getExternalFilesDir("folder")
The difference between these two are as follows
getExternalFilesDir():
This will return the path to files folder inside Android/data/data/your_package_name/. It is used to store any required files for your app.
getExternalStorageDirectory():
This returns the root path of you external SD card.
As far as the deprecation warning is concerned, Environment.getExternalStorageDirectory() or Environment.getExternalStoragePublicDirectory(), are deprecated. They still work, but will be removed soon, so you need to stop using those.
Related
I am learning android development and i am facing some problem to read getExternalStorageDirectory in java, I have read https://developer.android.com/reference/android/os/Environment but can't understand, can someone help me with example code in java.
From the docs you can see:
getExternalStoragePublicDirectory(String type)
This method was deprecated in API level 29. To improve user privacy, direct access to shared/external storage devices is deprecated. When
an app targets Build.VERSION_CODES.Q, the path returned from this
method is no longer directly accessible to apps. Apps can continue to
access content stored on shared/external storage by migrating to
alternatives such as Context#getExternalFilesDir(String),
MediaStore, or Intent#ACTION_OPEN_DOCUMENT.
Pass nothing as parameter to this function to get your directory as a File object :
context.getExternalFilesDir();
Here "Context" is an object which is obtained by this.getContext();
this is the current object of the Activity. Do check the scope carefully while using it.
Important
To access the Internal storage, Manifest.permission.WRITE_EXTERNAL_STORAGE and/or Manifest.permission.READ_EXTERNAL_STORAGE are required in the file AndroidManifest.xml.
Optional information:
Usually the internal storage has the path
/sdcard/ on Android devices. It's not a real path but a symlink.
It's confusing but "external sdcard" in Android acutally means the Internal device storage and not the external ejectable out-of-the-device memory card storage.
Also note that the real external sdcard cannot be fully access
Activity class extends the Context class That's why we can get the context from it.
Updated
From Android 11, it won't allow creating folder/file in the root directory but, we can still manage folder separation with help of the public directory (It will show a deprecated warning but it will work)
fun getAbsolutePath(context: Context): File {
return File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "{YOUR_FOLDER_NAME}"))
}
Now, create the file with common directory path
val file = File(getAbsolutePath(requireContext()), "FILENAME.EXTENSION")
Older
Use this static method.
Currently I don't find any legal way to do this.
So, I was made this static method to get root or getAbsolutePath file path.
public static File getAbsoluteDir(Context ctx, String optionalPath) {
String rootPath;
if (optionalPath != null && !optionalPath.equals("")) {
rootPath = ctx.getExternalFilesDir(optionalPath).getAbsolutePath();
} else {
rootPath = ctx.getExternalFilesDir(null).getAbsolutePath();
}
// extraPortion is extra part of file path
String extraPortion = "Android/data/" + BuildConfig.APPLICATION_ID
+ File.separator + "files" + File.separator;
// Remove extraPortion
rootPath = rootPath.replace(extraPortion, "");
return new File(rootPath);
}
Use getExternalFilesDir(), getExternalCacheDir(), or getExternalMediaDirs()
(methods on Context) instead of Environment.getExternalStorageDirectory()
String root = mContext.getExternalFilesDir(null).getAbsolutePath();
File myDir = new File(root + "/" + mContext.getResources().getString(R.string.app_name) + "_share");
myDir.mkdirs();
In 2022, Environment.getExternalStorageDirectory and Environment.getExternalStoragePublicDirectory are undeprecated.
They had been marked as deprecated from API 29 ~ 31, but not above.
I want to get the URIs to the entries of a zip file in order to keep references to it's contents without having to keep the zip file open.
Therefore I open the zip file using the zip filesystem and export the Path of the entries as URI.
Path zipfile = ...
URI uriOfFileInZip;
try(FileSystem fs = FileSystems.newFileSystem(zipfile, null)){
Path fileInZip = fs.getPath("fileInZip.txt");
uriOfFileInZip = fileInZip.toUri();
}
Now I want to read the file again, so I try to open a stream to the file.
InputStream is = uriOfFileInZip.toURL().openStream();
This works as long as the path of the zip file does not contain any spaces. As soon as it contains spaces, I get an error like this
java.io.FileNotFoundException: D:\example\name%20of%20zipfile.zip (The system cannot find the file specified)
the URI to the file in the zip is
jar:file:///D:/example/name%2520of%2520zipfile.zip!/fileInZip.txt
the name of the zip is
D:\example\name of zipfile.zip
I wonder about the %2520 this seems like an issue with the URL encoding, but shouldn't this be handled transparently? Or is it a bug?
Any ideas to solve this problem?
Looks like a bug.
Seems as if com.sun.nio.zipfs.ZipPath.toUri() is either messed up, or I didn't read the corresponding RFC yet ;-). Played around with some other file names. There seems to be a double encoding going on for the zip file path, but not for the file entry in the zip.
Besides not using the URI-approach you could also build the URI yourself from scratch, but then you are not that flexible anymore. Or you just undo the unnecessary encoding:
String uriParts[] = uriOfFileInZip.toString().split("!");
uriParts[0] = URLDecoder.decode(uriParts[0], "UTF-8");
uriOfFileInZip = URI.create(String.join("!", uriParts));
But to be honest, I would rather try to omit the URI for zip files or if you really have to, rename the files beforehand ;-) Better yet: open a bug if it does not behave as stated in the corresponding RFCs.
You may also want to get some additional information from the following question regarding bug, etc.:
Java 7 zip file system provider doesn't seem to accept spaces in URI
EDIT (added proposal without URI):
You can also try to completely work with your Path instance (fileInZip) instead of the URI, as the path instance "knows" its filesystem.
As soon as you need access to the file inside the zip, you create a new FileSystem based on the information of the Path instance (fileInZip.getFileSystem()). I did not elaborate that completely, but at least the file store should contain all the necessary information to access the zip file again. With that information you could call something like FileSystems.newFileSystem(Paths.get(fileStoreName), null).
Then you can also use Files.newInputStream(fileInZip) to create your InputStream. No need to use URI here.
This is only reproducible with JDK 8. The later versions do not have this issue.
For the following code:
Map<String, String> env = new HashMap<>();
env.put("create", "true");
final FileSystem fs = FileSystems.newFileSystem(new URI("jar:file:/D:/path%20with%20spaces/junit-4.5.jar"), env);
System.out.println(fs.getPath("LICENSE.TXT").toUri()); `
I got the following output with JDK 1.8.0_212 :
jar:file:///D:/path%2520with%2520spaces/junit-4.5.jar!/LICENSE.TXT
whereas with JDK 11.0.3:
jar:file:///D:/path%20with%20spaces/junit-4.5.jar!/LICENSE.TXT
A search through the Java bug system shows that it had been fixed in JDK 9 with JDK-8131067 .
I'm working on a Java based OpenGL project, which needs to load textures at runtime.
It's a Maven managed project and when packaged all files are placed inside one .jar.
I have the following piece of code which tries to load a texture:
URL url = Utils.class.getResource("/car.jpg");
TextureIO.newTexture(new File(url.toURI()), false);
That code works fine if I run the project from within Eclipse, but when packaged and executed from command line I get the following error (which is referring to the second line above):
java.lang.IllegalArgumentException: URI is not hierarchical
How can that line be re-written so the URI is correct, assuming I need to use getResourceAsStream(), but I need to pass it's value into a new File()?
You are trying to access it as a File, but when you package it in a jar there is no car.jpg file anymore, it is zipped inside the jar file.
So, you can't crate a File object pointing to it, the URL in this case is not a normal file URL but a "special" file-inside-the-jar URL.
Java is able to decode these URLs, or to access the resource directly and give you an InputStream.
The newTexture method also accepts either directly the URL or an InputStream.
To use the URL :
TextureIO.newTexture(url, false,"jpg");
To use the input stream :
InputStream instr = null;
try {
instr = Utils.class.getResourceAsStream("/car.jpg");
TextureIO.newTexture(instr, false, "jpg");
} finally {
instr.close();
}
I have a java app where I'm trying to load a text file that will be included in the jar.
When I do getClass().getResource("/a/b/c/"), it's able to create the URL for that path and I can print it out and everything looks fine.
However, if I try getClass().getResource(/a/b/../"), then I get a null URL back.
It seems to not like the .. in the path. Anyone see what I'm doing wrong? I can post more code if it would be helpful.
The normalize() methods (there are four of them) in the FilenameUtils class could help you. It's in the Apache Commons IO library.
final String name = "/a/b/../";
final String normalizedName = FilenameUtils.normalize(name, true); // "/a/"
getClass().getResource(normalizedName);
The path you specify in getResource() is not a file system path and can not be resolved canonically in the same way as paths are resolved by File object (and its ilk). Can I take it that you are trying to read a resource relative to another path?
I am working on a web-based program, using Java. I am not sure exactly how to phrase this, but I expect the program to be running from within the c:/Resin/webapps/apps directory. However, when I reference a file in the program like this: "../files/randomfile.pdf", it cannot find that file. It works when I reference it like this: "c:/Resin/webapps/files/randomfile.pdf". How to I change the "running location"? (And what is the technical term for this?)
try {
Document iTextDoc = new Document(PageSize.LETTER, 27, 27, 35, 18);
HttpServletResponse res = (HttpServletResponse) pageContext.getResponse();
res.setContentType("application/vnd.ms-word");
res.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".rtf;");
RtfWriter2 rtfWriter = RtfWriter2.getInstance(iTextDoc, res.getOutputStream());
iTextDoc.open();
iTextDoc.add(new Paragraph ("Testing RTF Letterhead with Logo"));
// Use full classname to avoid ambiguity with java.awt.Image
com.lowagie.text.Image logoImg = com.lowagie.text.Image.getInstance("../files/someimage.jpg");
logoImg.setAlignment(Image.RIGHT | Image.TEXTWRAP);
iTextDoc.add(logoImg);
iTextDoc.add(new Paragraph ("Put other information about organization beneath logo"));
iTextDoc.close();
}
I get the following error with the resulting file: Adobe Reader could not open 'someFile.pdf' because it is either not a supported file type or because the file has been damaged (for example, it was sent as an email attachment and wasn't correctly decoded).
However, if I change the getInstance command to this:
com.lowagie.text.Image logoImg = com.lowagie.text.Image.getInstance("webapps/files/someimage.jpg");
it works. So my guess is that the working directory (thanks for the term) needs to be set somewhere. I am using Resin -- any idea where I should be setting this?
Thanks!
You are probably referring to the user.dir system property, which is read only. Relative paths use this as the root folder.
In a java program you can't change the running location.
First you should find out where your program is running. You can do this by calling
System.out.println( new File( "." ).getAbsolutePath() );
Now you can specify your paths relative to this directory.
From the java.io.File doc
By default the classes in the java.io package always resolve relative pathnames against the current user directory. This directory is named by the system property user.dir, and is typically the directory in which the Java virtual machine was invoked.
Broadly speaking, you should not rely on the current directory for locating files. Use full (absolute) paths, if possible (but without harcoding it in you application, of course). if the file is inside a webapp tree (or just inside the classpath), you might want to take a look at findResource() and related methods.