I have an app that can open and edit binary file from external SD card. I want to have possibility to save this file back from where it was opened.
I have added permissions in manifest file and I also ask user for permission.
Thanks to those permissions I can open file and get the data but when I want to save file to external SD card there is an error: java.io.FileNotFoundException: /storage/3834-3433/file.bin: open failed: EACCES (Permission denied).
Here is code for granting permission:
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else {
return true;
}
}
Here is the code for choosing and getting file path:
button.setOnClickListener(v -> {
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
startActivityForResult(intent, 200);
});
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 200:
if (resultCode == RESULT_OK) {
String filePath = Objects.requireNonNull(data.getData()).getPath();
filePathMain = filePath;
}
break;
}
}
And this is the part of code to save file:
void byteArrayToFile() {
try (OutputStream out = new FileOutputStream(filePathMain)) {
out.write(outBytes);
} catch (IOException e) {
e.printStackTrace();
}
}
I have no idea why it allows me to open file but not to write when I have <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />.
I also had to run this app on the real device because when I run this on emulator there is an error that it can't find the file I choose.
I could really use some help on this. Thank You.
Based on #CommonsWare comment I changed from ACTION_GET_CONTENT to ACTION_OPEN_DOCUMENT to get a file Uri.
Also used ContentResolver to read:
InputStream inputStream = getContentResolver().openInputStream(uri);
and write to file:
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
And now it's working properly.
Related
I have build sample app for user to write some text and save it on internal storage.
Format TXT or pdf. I have tried on android Kitkat sdk 19 and It's working fine but when I tried on the latest android 9 I got denied could not save the text in internal storage
Edit : I wonder If have to manually create My files folder
What I'm I missing ?
AndroidManifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSaveTextBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
mText = mResultEt.getText().toString().trim();
if(mText.isEmpty()){
Toast.makeText(MainActivity.this, "write text First...", Toast.LENGTH_SHORT).show();
}
else{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED){
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions,WRITE_EXTERNAL_STORAGE_CODE);
}else {
saveToTxtFile(mText);
}
}else {
saveToTxtFile(mText);
}
}
}
});
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode){
case WRITE_EXTERNAL_STORAGE_CODE: {
if (grantResults.length > 0 && grantResults[0]
== PackageManager.PERMISSION_GRANTED) {
//permission granted save data
saveToTxtFile(mText);
} else {
//permission denied
Toast.makeText(this, "Storage Permission required to store", Toast.LENGTH_SHORT).show();
}
}
break;
}
}
private void saveToTxtFile(String mText){
//get current time for file name
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(System.currentTimeMillis());
try{
//path tp storage
File path = Environment.getExternalStorageDirectory();
//create folder named "My file"
File dir = new File( path + "/My Files/");
dir.mkdirs();
//file name
String fileName = "MyFile_" + timestamp + ".txt";
File file = new File (dir, fileName);
//used to store characater in file
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(mText);
bw.close();
//show file name and path where file saved
Toast.makeText(this, fileName+"is saved to\n" +dir, Toast.LENGTH_SHORT).show();
}catch (Exception e){
//if anything goes wrong
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
As suggested both Shashanth and kashyap answers.
This is just a workaround but it works.
I have added
android:requestLegacyExternalStorage="true"
to my <application> element in the AndroidManifest.xml file.
And changed the line
File path = Environment.getExternalStorageDirectory();
to
File path = new File(getExternalFilesDir(null).getAbsolutePath());
In your MainActivity you should check if permission is granted or not, then use != operator.
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {}
this line should be-
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED){}
I am trying to store a String to a file which should be stored on the device
I have the following code that stores the String into a variable called exportstring:
final JSONArray jsonArray1 = jsonArray;
JSONObject export = jsonArray1.getJSONObject(index);
String exportString = export.toString();
writeToFile(exportString);
I then have a function called writeToFile
private void writeToFile(String content) {
try {
File file = new File(Environment.getExternalStorageDirectory() + "/test.txt");
if (!file.exists()) {
file.createNewFile();
}
FileWriter writer = new FileWriter(file);
writer.append(content);
writer.flush();
writer.close();
} catch (IOException e) {
}
}
when I run it, I get a message coming up saying "The file was saved"
but when I open a file manager, I do not see it anywhere.
where is the file saved to?
or have I done something wrong here?
To store the file in Downloads folder, change:
File file = new File(Environment.getExternalStorageDirectory() + "/test.txt");
to:
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/test.txt");
UPDATE: Before trying to save the file you should make sure you have the required permissions, in this case the permission to write to storage. Below is a short example of how to do that:
protected boolean checkPermission() {
int result1 = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (result1 == PackageManager.PERMISSION_GRANTED ) {
return true;
} else {
return false;
}
}
protected void requestPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, "Write External Storage permission allows us to do store files. Please allow this permission in App Settings.", Toast.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
}
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 100:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("permissionCheck", "Permission Granted, do what you want to do when user grants the permission here!!!");
} else {
Log.d("permissionCheck", "Permission Denied, do what you want to do when user denies the permission here...");
}
break;
}
}
Include the code above to your activity (or any activity before the one you currently are at) and before trying to store the string to the file do the following:
if(!checkPermission()){
Log.d("permissionCheck", "Need to get the permissions");
requestPermission();
}else{
Log.d("permissionCheck", "Already have the permissions");
}
To see the logs in logcat :
I am coding an Android app (in Java) which uses OCR to convert handwriting into digital text. I am trying to take the String generated by the OCR function in my code and write it to a text file (the OCR portion is currently working). I would then like to create a folder (in phone's external storage, for example My Files on Samsung) and add the text file to this folder, which contains only the files the user has created (which the user should be able to access and share).
I have conducted some research on writing to phone's external storage (including other StackOverflow questions) but no tutorial has worked for me.
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
public File writeFolder ()
{
File file = null;
if (isExternalStorageWritable())
{
// Get the directory for the user's public directory.
file = new File(Environment.getExternalStorageDirectory() + File.separator + "OCR Documents");
}
if (!file.mkdirs())
Log.e(LOG_TAG, "Directory not created");
else
System.out.println(file.getAbsolutePath());
return file;
}
The code above is what I have, however after testing it, the AbsolutePath is null. It does not seem to be creating a folder on the phone's external storage. How would I go about this so that a folder is created and I can add files to that folder?
Your code to create the directory is fine.
But there's a chance you're missing permissions due to newer versions of Android requiring a User's consent before you can write files to the external storage.
First, make sure you have this permission in your Manifest.xml:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
Afterwards, since WRITE_EXTERNAL_STORAGE is listed as a Dangerous Permission, as seen here: https://developer.android.com/guide/topics/permissions/overview#normal-dangerous , you'll also need to explicitly request the permission from the user.
Finally, to request the permission:
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
// Permission has already been granted
}
You'll also need to handle the response of the request:
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request.
}
}
The above code was copied from: https://developer.android.com/training/permissions/requesting
You should read that link more thoroughly since it provides a good explanation of what you need to explain to the user, since users are typically very wary when Apps ask for permissions to modify files in their storage.
You can try this,
private void getWirtePermissionAndCreateDir() {
if (Build.VERSION.SDK_INT < 23) {
createDir();
} else {
final String[] PERMISSIONS_STORAGE = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
//Asking request Permissions
ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, 9);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
boolean writeAccepted = false;
switch (requestCode) {
case 9:
writeAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
if (writeAccepted) {
createDir();
} else {
Toast.makeText(MainActivity.this, "You don't assign permission.", Toast.LENGTH_LONG).show();
}
}
private void createDir(){
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "OCR Documents");
file.mkdirs();
Toast.makeText(MainActivity.this, file.getAbsolutePath(), Toast.LENGTH_LONG).show();
}
You have to add getWirtePermissionAndCreateDir() instead of writeFolder() in activity body.
Below function will create folder and then create file in that folder and if folder already exists then simply create file.
private FileOutputStream fos;
//Function: create a file in a folder
private boolean createFileInFolder(String fileName, String folderName) {
if (isExternalStorageWritable()) {
String path = Environment.getExternalStorageDirectory() + "/" + folderName;
File folder = new File(path);
if (!folder.exists()) {
folder.mkdirs();
}
txtFile = new File(path, fileName);
try {
fos = new FileOutputStream(txtFile);
return true;
} catch (IOException e) {
Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
return false;
}
} else
return false;
}
//Function: IsExternalStorageWritable?
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
You may need to check permissions so also define these functions.
private boolean permission;
private final int MY_PERMISSIONS_REQUEST = 10;
//Function: checkPermission
private void checkPermission() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST);
} else {
permission = true;
}
}
//Function: Permission Request Results
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permission = true;
} else {
permission = false;
}
return;
}
}
}
In your manifest file don't forget to add this line.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I'm trying to save an image on JPG format on a specific folder from my gallery. But my code is not creating a directory, whenever i create a Toast it return for me /storage/emulated/0/DCIM/MyFodler,but when will i open the gallery, this foder not exist. I'm building the application direct of my devide with Android Marshmallow 6.0.
Code to create Bitmap:
private Bitmap getToBitmap(ImageView view, int Width, int Heigth){
Bitmap bitmap = Bitmap.createBitmap(Width,Heigth, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
Code to try save the image on gallery:
private void TrySaveMediaStore(){
String path = Environment.getExternalStorageDirectory().toString();
OutputStream FileOut = null;
File file = new File(path,"DCIM/MyFolder");
file.mkdirs();
Toast.makeText(getApplicationContext(),file.getAbsolutePath(),Toast.LENGTH_SHORT).show();
try{
FileOut = new FileOutputStream(file);
FileOut.flush();
FileOut.close();
Bitmap bitmap = getToBitmap(img,img.getMaxWidth(),img.getMaxHeight());
bitmap.compress(Bitmap.CompressFormat.JPEG,100,FileOut);
MediaStore.Images.Media.insertImage(getContentResolver(), file.getAbsolutePath(), file.getName(), file.getName());
Toast.makeText(this,file.getAbsolutePath(),Toast.LENGTH_SHORT).show();
}catch (FileNotFoundException e){
return;
}catch (IOException e){
e.printStackTrace();
}
}
Androidmanifest permissions:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
DCIM/MyFolder is a directory. You create this as a directory using mkdirs().
You cannot then try using DCIM/MyFolder as a filename for saving a JPEG. You need to create a file inside the directory.
So, instead of:
FileOut = new FileOutputStream(file);
use something like:
File theActualImageFile=new File(file, "something.jpeg");
FileOut = new FileOutputStream(theActualImageFile);
Also:
You need to deal with runtime permissions, if your targetSdkVersion is 23 or higher
A gallery app will see neither the directory nor the file, until you tell the MediaStore to index the newly-created JPEG
i think a had the same problem, actually the image do insert just fine in the memory, but when i tried to watch it didn't show as i expected, i solved it refreshing the gallery with the scanner class, used this code:
MediaScannerConnection.scanFile(this,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
See this link for more info: How can I refresh the Gallery after I inserted an Image in android?
You may use the below code for asking runtime storage permission:
final int MyVersion = Build.VERSION.SDK_INT;
if (MyVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (!checkIfAlreadyhavePermission()) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
TrySaveMediaStore() ;
}
checkIfAlreadyhavePermission() method:
private boolean checkIfAlreadyhavePermission() {
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED;
}
Add onRequestPermission():
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
TrySaveMediaStore();
} else {
Toast.makeText(this, "Please give your permission.", Toast.LENGTH_LONG).show();
}
break;
}
}
}
After creating the file scan MediaStore:
public void scanFile(Context c, File file, String mimeType) {
MediaScannerConnection
.scanFile(c, new String[] {file.getAbsolutePath()},
new String[] {mimeType}, null);
}
Yes, the problem is the media scanner. Yo can simply check the file using a terminal (download the app if you don't have it) and go manually to the directory. I had the same problem, but at least I know the file is there.
I'm trying to write data in the file and store it in the database. I tried out lots of codes online. But, I couldn't write the data. Can anyone please help me out. As of now I'm using the following code. I declared onClick="writemessage" and using the below code in the writemessage(View view) function. I'm running by connecting the mobile to the laptop. Can anyone please help me out.
FileOutputStream fos = null;
try {
String filename = "abc.txt";
String data = "Sensor data is found";
File myFile = new File(Environment
.getExternalStorageDirectory(), filename);
if (!myFile.exists())
myFile.createNewFile();
byte[] data = string.getBytes();
try {
fos = new FileOutputStream(myFile);
fos.write(data);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Check AndroidManifest.xml for this permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
and in your activity handle run time permission
private int REQUEST_WRITE_EXTERNAL_STORAGE = 1;
Check for Write External Storage Permission before writing file on SD card.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
}else {
//Write Data to SD Card
}
and override onRequestPermission method of activity.
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
this.finish();
Toast.makeText(this, "Permission is required.", Toast.LENGTH_LONG).show();
}
return;
}
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
Have you added this permission in manifest file and handle run time permission.
< uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />