I am trying to create an app that will store some PDf files as base64 encoded Strings in a database and then later decode them and dispay them (with an Intent to open other PDF reader).
But something doesn't work properly. I know that the byte array is the same before and after storage as encoded String, so that isn't the problem.
I think the problem is somewhere in the process of creating a File to open with the intent, but I'm not sure.
Creating the String:
byte[] b = Files.toByteArray(pdf);
String encodedFile = Base64.encodeToString(b, Base64.DEFAULT);
pdf is the File I get from this:
else if (requestCode == PICK_PDF_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null)
{
Uri uri = data.getData();
try {
String fileName = uri.toString();
fileName = fileName.substring(fileName.length()-10);
service.addPDF(order, fileName, new File(uri.getPath()));
} catch (IOException e) {
e.printStackTrace();
}
updateFileList();
}
Getting File from String:
case PDF:
try {
byte[] pdfAsBytes = Base64.decode(file.getContent(), Base64.DEFAULT);
File dir = getStorageDir();
File pdffile = new File(dir, file.getName());
if(!pdffile.exists())
{
pdffile.getParentFile().mkdirs();
pdffile.createNewFile();
}
Files.write(pdfAsBytes, pdffile);
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.setDataAndType(Uri.fromFile(pdffile), "application/pdf");
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(pdfIntent);
} catch (IOException e) {
e.printStackTrace();
}
break;
This code runs with no errors, but the PDF viewer cannot display the file. I have tried with several viewers. I suspect the resulting file
Turns out I needed to save to external storage instead of the dir.
File dir = getStorageDir();
Should be
File dir = Environment.getExternalStorageDirectory();
Then it works.
Related
I am using ActivityResultLauncher to fetch uri of a file. Now I want to modify that file using fileoutputStream but can't, since fileoutputstream need path string. Here is a code that I am using to get File Uri:
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
Uri uri = data.getData();//this gets me URI
}
}
});
public void openFileChooser()
{
Intent data = new Intent(Intent.ACTION_GET_CONTENT);
data.addCategory(Intent.CATEGORY_OPENABLE);
data.setType("*/*");
data = Intent.createChooser(data, "Choose a file");
Intent intent = Intent.createChooser(data, "Choose a file");
someActivityResultLauncher.launch(intent);
}
The code works perfect for picking and reading file. But I am not able to use below mentioned function to write same file using:
static void writeToFile(String path,String data,Context context) {
try {
FileOutputStream output = null;
output = new FileOutputStream(path, false);
output.write(data.getBytes());
output.close();
Toast.makeText(getApplicationContext(),"SAVED",Toast.LENGTH_LONG).show();
}
catch (IOException e) {
Toast.makeText(getApplicationContext(),"File write failed: " + e.toString(),Toast.LENGTH_LONG).show();
}
}
I have tried uri.getPath() but it is returning /document/msf:21. That is some complicated uri string and can't be used as file path. I have also been trying to use realPath functions mentioned in stackflow and other forums but do not find it working (they may work but they are depending upon API levels that making them not reliable). So, what actually want to ask:
1- Is there any other way to pick file to get actual path?
2- If there is no way to pick path, how can I write to File using direct Uri? and not using getpath or path string.
3- Is there a way to use fileoutstream with Uri in this scenario where Uri is only available?
Thanks
OutputStream os = getContentResolver().openOutputStream(data.getData());
You need to pass it through input stream amd buffer reader with output stream.
Try this on start intent
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, "JWI.pdf");
startActivityForResult(intent, CREATE_FILE);
Try this on activity result
try {
uri = _data.getData();
muri = uri.toString();
t.edit().putString("Uri", muri).commit();
final int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
getContentResolver().takePersistableUriPermission(uri, takeFlags);
DocumentFile doc = DocumentFile.fromSingleUri(this, uri);
boolean doo = doc.exists();
if (!doo) {
DocumentFile f = doc.createFile("pdf/plain", "JAMZ.pdf");}
ContentResolver cr = getContentResolver(); OutputStream output =
cr.openOutputStream(uri);
java.io.InputStream asset = getAssets().open("JWI.pdf");
final byte[] buffer = new byte[1024];
int size;
while ((size = asset.read(buffer)) != -1) {
output.write(buffer, 0, size);
}
asset.close();
output.close();
} catch (Exception e) {}}}
This is the code i used to get the uri of my image
Uri imageUri = data.getData();
How do I get the actual path of an image selected?
the Uri value/path that i am currently getting is
content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FLightStick%2F144pixels.bmp
the correct filepath of the image that i need is for my other function is
/storage/emulated/0/LightStick/144pixels.bmp
The image selection function:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK){//everything processed successfully
if(requestCode == IMAGE_GALLERY_REQUEST){ //hearing back from image gallery
//the address of the image on the SD card
Uri imageUri = data.getData();
BMPfilepath =imageUri.getPath();
//stream to read image data from SD card
InputStream inputStream;
try {
inputStream = getContentResolver().openInputStream(imageUri);//getting an input stream, based no the URI of image
Bitmap image = BitmapFactory.decodeStream(inputStream);//get bitmap from stream
imgPicture.setImageBitmap(image);//show image to user in imageview
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(this, "Unable to open image", Toast.LENGTH_LONG).show(); //let user know image unavail
}//catch
} //requestCode == IMAGE_GALLERY_REQUEST
}
The upload function which uses the imageUri from the previous function.
String path = imageUri.getPath().toString(); the app crashes and goes to a looper file when in debug
public void onUploadToArduino(){
String path = imageUri.getPath().toString();//<- app crashes and goes to a looper file when in debug
String sdpath = System.getenv("EXTERNAL_STORAGE");
String extStore = Environment.getExternalStorageDirectory().getPath();
String FILENAME = extStore + "/LightStick/144pixels.bmp";
String collected = null;
FileInputStream fis = null;
FileInputStream fileInputStream = null;
byte[] bytesArray = null;
try {
File file = new File(FILENAME);//<- this works
//File file = new File(path);//<- this doesnt
bytesArray = new byte[(int) file.length()];
//read file into bytes[]
fileInputStream = new FileInputStream(file);
fileInputStream.read(bytesArray);
How do I get the actual path of an image selected?
You don't. A Uri is not a file and may not point to a file, let alone one that you can access.
Use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri. Ideally, just use the stream. If you need a File for some other API that is poorly written and does not support streams:
Create a FileOutputStream on some File that you control (e.g., in getCacheDir())
Use the InputStream and the FileOutputStream to copy the bytes to your file
Use your file
I have not found the answer to this question anywhere.
The Bitmap Image is processed in The application, meaning there is no File path to get the Image.
Below is how to convert a Uri to Bitmap
if (requestCode == RC_PHOTO_PICKER && resultCode == RESULT_OK) {
Uri selectedImageUri = data.getData();
imageview.setImageURI(selectedImageUri);
try {
bitmap1 = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImageUri);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "" + e, Toast.LENGTH_SHORT).show();
}
bitmap1.compress(Bitmap.CompressFormat.JPEG, 7, bytearrayoutputstream);
BYTE = bytearrayoutputstream.toByteArray();
bitmap2 = BitmapFactory.decodeByteArray(BYTE, 0, BYTE.length);
imagetoo.setImageBitmap(bitmap2);
}
How do I now reconvert to a Uri
URI is super set of URL that means its a path to file . whereas Bitmap is a digital image composed of a matrix of dots.Bitmap represents a data and uri represents that location where data is saved .SO if you need to get a URI for a bitmap You just need to save it on a storage . In android you can do it by Java IO like below:First Create a file where you want to save it :
public File createImageFile() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File mFileTemp = null;
String root=activity.getDir("my_sub_dir",Context.MODE_PRIVATE).getAbsolutePath();
File myDir = new File(root + "/Img");
if(!myDir.exists()){
myDir.mkdirs();
}
try {
mFileTemp=File.createTempFile(imageFileName,".jpg",myDir.getAbsoluteFile());
} catch (IOException e1) {
e1.printStackTrace();
}
return mFileTemp;
}
Then flush it and you will get the URi
File file = createImageFile(context);
if (file != null) {
FileOutputStream fout;
try {
fout = new FileOutputStream(file);
currentImage.compress(Bitmap.CompressFormat.PNG, 70, fout);
fout.flush();
} catch (Exception e) {
e.printStackTrace();
}
Uri uri=Uri.fromFile(file);
}
This is just an example not idle code for all android version. To use Uri above and on android N you should use FileProvider to serve the file . Follow the Commonsware's answer.
Use compress() on Bitmap to write the bitmap to a file. Then, most likely, use FileProvider to serve that file, where getUriForFile() gives you the Uri corresponding to the file.
IOW, you do not "convert" a bitmap to a Uri. You save the bitmap somewhere that gives you a Uri.
I try to open a PDF in my application.
First, I create the PDF like that :
String filename = Environment.getExternalStorageDirectory().toString()+"/mypdf.pdf";
File file = new File(filename);
try {
FileOutputStream bos = new FileOutputStream(file);
bos.write(Base64.decode(base64, 0));
bos.flush();
bos.close();
} catch (IOException e) {
Log.e(TAG, "IOError with PDF");
e.printStackTrace();
}
Intent intent = new Intent(this, PdfActivity.class);
intent.putExtra("file", filename);
startActivity(intent);
The file is well created and readable, I can open this with ESExplorer application.
This file is located in /storage/emulated/0/myfile.pdf
in the PdfActivity I try to open the PDF :
Bundle extras = getIntent().getExtras();
String url = extras.getString("file");
File file = new File(url);
try {
if (file.exists()) {
Uri path = Uri.parse(url);
Intent objIntent = new Intent(Intent.ACTION_VIEW);
objIntent.setDataAndType(path, "application/pdf");
objIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(objIntent);
} else {
Toast.makeText(this, "File NotFound", Toast.LENGTH_SHORT).show();
}
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "No Viewer Application Found", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
file.exists() return true, Intent start, but my PDF reader says : "File not found"
I've added read and write permissions on external storage.
Does someone have any idea why it can't access to my file ?
objIntent.setDataAndType(url, "application/pdf");
use above line in your second snippest also declare String url as a global variable hope it will help you if it will not work try to use hardcode value of file path and see is it working or not ?
and are you sure file is exist? check this scenario tooo :)
I found the solution.
I've replace the Uri like that:
Uri path = Uri.fromFile(file);
And it works!
I am using following code to download and read a PDF file from internal storage on device.
I am able to download the files successfully to the directory:
data/data/packagename/app_books/file.pdf
But I am unable to read the file using a PDF reader application like Adobe Reader.
Code to download file
//Creating an internal dir;
File mydir = getApplicationContext().getDir("books", Context.MODE_WORLD_READABLE);
try {
File file = new File(mydir, outputFileName);
URL downloadUrl = new URL(url);
URLConnection ucon = downloadUrl.openConnection();
ucon.connect();
InputStream is = ucon.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
byte data[] = new byte[1024];
int current = 0;
while ((current = is.read(data)) != -1) {
fos.write(data, 0, current);
}
is.close();
fos.flush();
fos.close();
isFileDownloaded=true;
} catch (IOException e) {
e.printStackTrace();
isFileDownloaded = false;
System.out.println(outputFileName + " not downloaded");
}
if (isFileDownloaded)
System.out.println(outputFileName + " downloaded");
return isFileDownloaded;
Code to read the file
PackageManager packageManager = getPackageManager();
Intent testIntent = new Intent(Intent.ACTION_VIEW);
testIntent.setType("application/pdf");
List list = packageManager.queryIntentActivities(testIntent,
PackageManager.MATCH_DEFAULT_ONLY);
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
File fileToRead = new File(
"/data/data/com.example.filedownloader/app_books/Book.pdf");
Uri uri = Uri.fromFile(fileToRead.getAbsoluteFile());
intent.setDataAndType(uri, "application/pdf");
startActivity(intent);
} catch (Exception ex) {
Log.i(getClass().toString(), ex.toString());
Toast.makeText(MainActivity.this,
"Cannot open your selected file, try again later",
Toast.LENGTH_SHORT).show();
}
All works fine but the reader app says "File Path is not valid".
Your path is only valid for your app. Place the file in a place where other apps can 'see' it. Use GetExternalFilesDir() or getExternalStorageDirectory().
Note about files which are created inside the directory created by Context.getDir(String name, int mode) that they will only be accessible by your own application; you can only set the mode of the entire directory, not of individual files.
So you can use Context.openFileOutput(String name, int mode). I'm re-using your code for an example:
try {
// Now we use Context.MODE_WORLD_READABLE for this file
FileOutputStream fos = openFileOutput(outputFileName,
Context.MODE_WORLD_READABLE);
// Download data and store it to `fos`
// ...
You might want to take a look at this guide: Using the Internal Storage.
If you would like to keep the file app specific, you can use PdfRenderer available for Lollipop and above builds. There are great tutorials on google and youtube that work well. The method you are using is a secure way to store a PDF file that is only readable from inside the app ONLY. No outside application like Adobe PDF Reader will be able to even see the file.It took me a lot of seaching but I found a solution to my specific usage by using this site and especially youtube.
How to download PDF file from asset folder to storage by making folder
make sure you have storage permission are given like marshmallow device support etc then follow these steps
private void CopyReadAssets()
{
AssetManager assetManager = getContext().getAssets();
FileInputStream in = null;
FileOutputStream out = null;
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(Environment.getExternalStorageDirectory()+File.separator+ "A_level");
File dir2;
if (dir.exists() && dir.isDirectory()){
Log.e("tag out", ""+ dir);
}else {
dir.mkdir();
Log.e("tag out", "not exist");
}
File file = new File(dir, mTitle+".pdf");
try
{
Log.e("tag out", ""+ file);
out = new FileOutputStream(file);
in = new FileInputStream (new File(mPath));
Log.e("tag In", ""+ in);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e)
{
Log.e("tag out", ""+ out);
Log.e("tag In", ""+ in);
Log.e("tag", e.getMessage());
Log.e("tag", ""+file);
Log.i("tag",""+sdcard.getAbsolutePath() + "A_level");
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}