In my app the user can select a picture from their gallery as avatar, but i want to save it into my app storage so they can delete the file.
My code is:
//onActivityResult()
else if (requestCode == SELECT_PICTURE)
{
mFile = new File(getRealPathFromURI(data.getData()));
Date d = new Date();
long ms = d.getTime();
mName = String.valueOf(ms) + ".jpg";
copyfile(mFile,mName);
File file = new File(Environment.getExternalStorageDirectory(), mName);
Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
imgPhoto.setImageBitmap(myBitmap);
}
public String getRealPathFromURI(Uri contentUri)
{
// can post image
String [] proj={MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery( contentUri,proj,null,null,null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
private void copyfile(File file,String newFileName){
try{
InputStream in = new FileInputStream(file);
OutputStream out = openFileOutput(newFileName, MODE_PRIVATE);
byte[] buf = new byte[4096];
int len;
while ((len = in.read(buf)) > 0){
out.write(buf, 0, len);
}
in.close();
out.close();
Log.d(null,"success");
}
catch(FileNotFoundException ex){
ex.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
}
If i decode mFile in the Bitmap, the image is shown, so mFile has the image. ¿Any ideas?
Well, first of all... you haven't told us the behavior you are currently getting. Does your app crash? Does the picture not get displayed? Some other unexpected behavior?
Other than that:
Don't use managedQuery()... it's run on the main UI thread so it can easily introduce lag into your application. Ideally you'd want to use a CursorLoader but it might be easier to just wrap all of your work in an AsyncTask (by "all of your work" I mean all of the work related to saving/retrieving/decoding the image file... and I suggest this because it can take a considerable amount of time to do all of this stuff, and your app might look slow if the UI thread is blocked for too long).
If you do choose to wrap your work in an AsyncTask (which I suggest you do), put all of the work in doInBackground() and update your UI accordingly when you are finished in onPostExecute().
Related
final String AUTHORITY = "com.android.externalstorage.documents";
Uri roottree = DocumentsContract.buildTreeDocumentUri(AUTHORITY,"primary:");
Uri sourceuri = DocumentsContract.buildDocumentUriUsingTree(roottree,DocumentsContract.geTreeDocumentId(roottree) + "Folder1");
Uri TargetUri = DocumentsContract.buildDocumentUriUsingTree(roottree,DocumentsContract.getTreeDocumentId(roottree) + "Folder2");
Uri resulturi = DocumentsContract.copyDocument(myContentResolver,sourceuri,TargetUri);
Copying Folder1 into Folder2 always return null. CreateDocument, DeleteDocument even MoveDocument working without any issue.
I believe it was a deliberately bug.
It didn't work and you need to rebuild the function.
Here is simple sample:
public boolean copyFileUri(Uri FilePath, Uri ToFolder, String Name){boolean done=true;
try {
InputStream in = this.getContentResolver().openInputStream(FilePath);
Uri uriOut=DocumentsContract.createDocument(getContentResolver(), ToFolder, "text/plain", Name );
OutputStream out = new FileOutputStream(getContentResolver().openFileDescriptor(uriOut, "w").getFileDescriptor());
Uri uRename=DocumentsContract.renameDocument(getApplicationContext().getContentResolver(), uriOut, Name );
if (uRename==null){/*RENAME WITH WHILE COUNTER*/}
try { byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } } catch(Exception e){done=false;}
out.close(); in.close();
} catch(Exception e){done=false;} return done;
}
Note that you will need to DIY additional function for these case:
The destination folder have files with the same name, you need to add counter for rename or a switch to overwrite old file.
If you want to copy folder, it will a little more complex by add:
Detect Uri was folder or normal file
Create folder
Scan File
Recursion calling itself when scan so it scan whole the tree.
In our app users have been uploading millions of images for years using (roughly) this code:
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(postFilePath, bmOptions);
Bitmap roughBitmap = BitmapFactory.decodeFile(postFilePath, bmOptions);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
roughBitmap.compress(Bitmap.CompressFormat.JPEG, 70, stream);
InputStream fis = new ByteArrayInputStream(stream.toByteArray());
int fileSize = stream.toByteArray().length;
conn.setRequestProperty("Content-Length", Integer.toString(fileSize));
conn.setFixedLengthStreamingMode(fileSize);
...
if (fis != null) {
byte[] buf = new byte[10240];
int read;
while ((read = fis.read(buf)) > 0) {
os.write(buf, 0, read);
totalBytesRead += read;
if (uploadProgressListener != null) {
try {
uploadProgressListener.onBytesUploaded(read);
} catch (Exception e) {
Log.e(e);
}
}
}
fis.close();
}
Recently we saw the need to preserve the Exif data of uploaded images. The problem is that the image Exif data is lost when compressing the bitmap. I thought of using ExifInterface for extracting this data from the original file:
ExifInterface oldExif = new ExifInterface(postFilePath);
String value = oldExif.getAttribute(ExifInterface.TAG_DATETIME);
..and then adding it to the InputStream fis and then continue uploading the file. The problem is that ExifInterface cannot save Exif data to an InputStream.
How can Exif data be retained in the images when they'er uploaded to the server?
It's not a duplicate:
Just to clarify deeper, I've tried using the suggested duplicate question by using this method:
public static void copyExif(String originalPath, InputStream newStream) throws IOException {
String[] attributes = new String[]
{
ExifInterface.TAG_DATETIME,
ExifInterface.TAG_DATETIME_DIGITIZED,
ExifInterface.TAG_EXPOSURE_TIME,
ExifInterface.TAG_FLASH,
ExifInterface.TAG_FOCAL_LENGTH,
ExifInterface.TAG_GPS_ALTITUDE,
ExifInterface.TAG_GPS_ALTITUDE_REF,
ExifInterface.TAG_GPS_DATESTAMP,
ExifInterface.TAG_GPS_LATITUDE,
ExifInterface.TAG_GPS_LATITUDE_REF,
ExifInterface.TAG_GPS_LONGITUDE,
ExifInterface.TAG_GPS_LONGITUDE_REF,
ExifInterface.TAG_GPS_PROCESSING_METHOD,
ExifInterface.TAG_GPS_TIMESTAMP,
ExifInterface.TAG_MAKE,
ExifInterface.TAG_MODEL,
ExifInterface.TAG_ORIENTATION,
ExifInterface.TAG_SUBSEC_TIME,
ExifInterface.TAG_WHITE_BALANCE
};
ExifInterface oldExif = new ExifInterface(originalPath);
ExifInterface newExif = new ExifInterface(newStream);
if (attributes.length > 0) {
for (int i = 0; i < attributes.length; i++) {
String value = oldExif.getAttribute(attributes[i]);
if (value != null)
newExif.setAttribute(attributes[i], value);
}
newExif.saveAttributes();
}
}
.. but got the exception java.io.IOException: ExifInterface does not support saving attributes for the current input. after newExif.saveAttributes(); because I'm trying to save the attributes to an InputStream. How else can I do it?
My solution:
As #amuttsch and #CommonsWare suggested, I:
saved the scaled/compressed bitmap to a temp file
copied the exif from the original file to the temp file
converted the temp file to a byte array and sent it to upload
.. then I found out that the server strips the Exif again while generating image variants :-P but that's another story which server guys are now working to correct.
Main code:
...
// Copy original Exif to scaledBitmap
String tempFilePath = getTempFilePath(postFilePath);
try {
FileOutputStream out = new FileOutputStream(tempFilePath);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 70, out);
copyExif(postFilePath, tempFilePath);
} catch (Exception e) {
e.printStackTrace();
}
// Get stream from temp (exif loaded) file
File tempFile = new File(tempFilePath);
byte[] byteFile = readFile(tempFile);
fis = new ByteArrayInputStream(byteFile);
// Remove the temp file
boolean deleted = tempFile.delete();
// Finalize
int fileSize = byteFile.length;
conn.setRequestProperty("Content-Length", Integer.toString(fileSize));
conn.setFixedLengthStreamingMode(fileSize);
...
getTempFilePath():
private String getTempFilePath(String filename) {
String temp = "_temp";
int dot = filename.lastIndexOf(".");
String ext = filename.substring(dot + 1);
if (dot == -1 || !ext.matches("\\w+")) {
filename += temp;
} else {
filename = filename.substring(0, dot) + temp + "." + ext;
}
return filename;
}
copyExif():
public static void copyExif(String originalPath, String newPath) throws IOException {
String[] attributes = new String[]
{
ExifInterface.TAG_DATETIME,
ExifInterface.TAG_DATETIME_DIGITIZED,
ExifInterface.TAG_EXPOSURE_TIME,
ExifInterface.TAG_FLASH,
ExifInterface.TAG_FOCAL_LENGTH,
ExifInterface.TAG_GPS_ALTITUDE,
ExifInterface.TAG_GPS_ALTITUDE_REF,
ExifInterface.TAG_GPS_DATESTAMP,
ExifInterface.TAG_GPS_LATITUDE,
ExifInterface.TAG_GPS_LATITUDE_REF,
ExifInterface.TAG_GPS_LONGITUDE,
ExifInterface.TAG_GPS_LONGITUDE_REF,
ExifInterface.TAG_GPS_PROCESSING_METHOD,
ExifInterface.TAG_GPS_TIMESTAMP,
ExifInterface.TAG_MAKE,
ExifInterface.TAG_MODEL,
ExifInterface.TAG_ORIENTATION,
ExifInterface.TAG_SUBSEC_TIME,
ExifInterface.TAG_WHITE_BALANCE
};
ExifInterface oldExif = new ExifInterface(originalPath);
ExifInterface newExif = new ExifInterface(newPath);
if (attributes.length > 0) {
for (int i = 0; i < attributes.length; i++) {
String value = oldExif.getAttribute(attributes[i]);
if (value != null)
newExif.setAttribute(attributes[i], value);
}
newExif.saveAttributes();
}
}
readFile():
public static byte[] readFile(File file) throws IOException {
// Open file
RandomAccessFile f = new RandomAccessFile(file, "r");
try {
// Get and check length
long longlength = f.length();
int length = (int) longlength;
if (length != longlength)
throw new IOException("File size >= 2 GB");
// Read file and return data
byte[] data = new byte[length];
f.readFully(data);
return data;
} finally {
f.close();
}
}
The problem is that the image Exif data is lost when compressing the bitmap
The EXIF data is lost when reading in the Bitmap. A Bitmap has no EXIF tags.
How can Exif data be retained in the images when they'er uploaded to the server?
Stop reading in the Bitmap. Just upload the contents of postFilePath as-is. It will contain whatever EXIF tags it contains.
My assumption is that you are reading in the Bitmap in the hope that saving it again in 70% JPEG quality will result in meaningful bandwidth savings. I suspect that you are not saving very much, and you may be increasing the bandwidth in some cases (e.g., postFilePath points to a PNG). Your costs are a chunk of CPU time, an increased risk of an OutOfMemoryError, and the loss of your EXIF tags.
If, instead, the convert-to-70%-JPEG is some sort of data normalization approach, do that work on the server, where you have more CPU power, more disk space, more RAM, and continuous power.
Source: https://stackoverflow.com/a/11572752/8252521
Answered by: https://stackoverflow.com/users/1592398/code-jaff
Convert the file to bitmap by
Bitmap bi = BitmapFactory.decode(filepath + "DSC00021.jpg");
You can specify options too, look at API documentation
Or if you want to exchange the meta data from one file to another,
sanselan will probably be the best choice. This would be much
helpful when you manipulating the image, for example re-size.
The sample code will guide you in a right direction.
You need to just create a new OutputStream to preserve the Exif Information. There is no need of creating a new File.
I'm newbie to Android, I'm trying to send some Images from Android to a RESTFul WCF.
By now I'm being able to select the Images from the Gallery and sending them to the Server.
the WCF is expecting the image as Stream
But I'm having problems with the Synced Images that get stored in the Tablet Like the Facebook or G+ photos. (I don't know if they are cached or something)
I'm using this function to get the path of the Image
public static String getRealPathFromURI(Context context, Uri contentUri) {
String path = null;
if (contentUri.getScheme().toString().compareTo("content")==0)
{
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
path = cursor.getString(column_index);
}
else
{
path = contentUri.getPath();
}
Log.i(TAG, path);
return path;
}
With that kind of images I get an internet path like:
https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash4/s2048x2048/432098_10151223392360790_398885469_n.jpg
Just for clarity and to remark. I get a "content" scheme.. so I get the path from the "if " something like:
content://com.sec.android.gallery3d.provider/picasa/item/5703464571893262194
To send it to the Server im using MultipartEntity, because I saw in others post here in SO to do so, like this:
((MultipartEntity) oInputEntity).addPart(
"fileContents",
new FileBody(new File(Utilities.getRealPathFromURI(context,
imageUri)),
"image/jpeg"));
With that kind of images I was getting a FileNotFoundEception I think it's because the image path is an Internet path, so the MultiPartEntity don't know how to retrieve it,
So I changed my method to download the image and now is working with this code
public static File getFileFromURI(Context context, Uri contentUri) {
String path = IntUtilities.getRealPathFromURI(context, contentUri);
Log.i(TAG, path);
final File file;
if (path.startsWith("http") || path.startsWith("/http") )
{
//if its an image form internet lets download it and save it in our directory and return that file
// Determine Uri of camera image to save.
final String fname = "BIR" + UUID.randomUUID() + ".jpg";
final File root = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "BIR");
root.mkdirs();
file = new File(root, fname);
try {
final URL url = new URL(path);
final HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(false);
urlConnection.connect();
final FileOutputStream fileOutput = new FileOutputStream(file);
final InputStream inputStream = urlConnection.getInputStream();
#SuppressWarnings("unused")
int downloadedSize = 0;
byte[] buffer = new byte[1024];
int bufferLength = 0;
while ((bufferLength = inputStream.read(buffer)) > 0) {
fileOutput.write(buffer, 0, bufferLength);
downloadedSize += bufferLength;
}
// close the output stream when done
fileOutput.close();
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
{
file = new File(path);
}
return file;
}
((MultipartEntity) oInputEntity).addPart(
"fileContents",
new FileBody(Utilities.getFileFromURI(context,
imageUri),
"image/jpeg"));
But I'm not comfortable with this solution, seems like double effort, I turned off my Wifi and 3g in the tablet, also turned off and on the tablet iself and I still see those images, so I'm guessing they got copied locally or cached on the tablet when they were synced for the first time. I looked for them when attached to my computer (in Windows Explorer) to see if they were there, but I dont see them, maybe I'm doing something wrong or dont know the storage folder.
The main reason that I dont like this solution is that if you don't have Internet on the moment, obviously the image will not be downloaded, and the app I'm making is supposed to work offline, and well.. the Image is there, there shouldn't be a request to internet to guess a local image.
Being said this, is there a way to find the real/physical path of this Photos that were synced, that have an http or https scheme to send this images using the MultiPartEntity?
Or another proper way to send this Images to the Server?
I really appreciate your help
from the chooser dialog , you can always get a bitmap
chooser-->$Result->getData() = imageUri
from the imageUri, get a Bitmap by running the following code:
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
Once you get the bitmap..,
you can put it to a fileSink
you can use a Hashmap to cache it in Memory
mBitMap = BitmapFactory.decodeStream(
mCR.openInputStream(imageUri), null, options);
OutputStream os = new FileOutputStream(f);
mBitMap.compress(Bitmap.CompressFormat.JPG, minVal, os);
mload.memoryCache.putbig(file.toURI().toURL().toString(), mBitMap);
and you can http POST the Bitmap directly by loading its ByteArray to the Entity...
case POST:
HttpPost httpPost = new HttpPost(url);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
float tmp = (float) 1024 * 1024 / bmp.getByteCount();
int minVal = (Math.round(tmp * 100) < 101) ? Math.round(tmp * 100): 100;
if (bmp.compress(Bitmap.CompressFormat.JPEG, minVal, stream)){
httpPost.setEntity(new ByteArrayEntity(stream.toByteArray()));
}else{ //TODO need to format actual message
handler.sendMessage(Message.obtain(handler,
HttpConnection.DID_ERROR, new Exception("ERR bitmap NG")));
}
background on the POST method is here
lazyloader project is a good template to use.
So why use mimeMultipartEntity with a file when you can operate directly on the bytes in the bitMap? As soon as you have a Uri, get a Bitmap and use the bitmap/ Uri pair for the basis of your interface to memCache, interface to HTTP POST, interface to fileSink used to retrieve local file when you have CacheMiss. This will help minimize doing everything on the basis of a file.
Think about using a Map [hashON(Uri.toString() :: bitMap] to store the images that you process locally. Then when you want to POST an image , you can just retrieve it from the map and POST its bytes directly in a "ByteArrayEntity".
This question already has an answer here:
I need to be able to store sound files for my application on sdcard
(1 answer)
Closed 2 years ago.
I found this code which appears to be what I need in that it will copy byte for byte a file to the SDCard.
But how do I use it? say I have a text file called mytext.txt where do I put it in my application? and how would I reference it? I am using Eclipse
public static final void copyfile(String srFile, String dtFile){
try{
File f1 = new File(srFile);
File f2 = new File(dtFile);
InputStream in = new FileInputStream(f1);
OutputStream out = new FileOutputStream(f2);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0){
out.write(buf, 0, len);
}
in.close();
out.close();
System.out.println("File copied to " + f2.getAbsolutePath());
} catch(FileNotFoundException ex){
System.out.println(ex.getMessage() + " in the specified directory.");
System.exit(0);
} catch(IOException e){
System.out.println(e.getMessage());
}
}
I would make a FileUtilities class or somesuch. Have you looked at the examples here?
http://download.oracle.com/javase/tutorial/essential/io/copy.html
http://www.java2s.com/Code/Java/File-Input-Output/FileCopyinJava.htm
You don't want to blindly execute this code. It looks like it's meant for a java console app. System Printlines do not go anywhere that the user would see in an android application. I do not know what System.exit() does in an Android application, but you don't want to do this either. Depending on your application, you may want to add a toast notification that a copy fails. You want to at least log this.
Depending on the size of files you are copying, you may want to do this in a background thread as to not clog up your UI.
Well, at first glance that appears to be a sound method, except that you'd want to replace the System.out print statements with an android Log method... but besides that you could copy/paste that and include that method in a class.
To use it, however... you should have a look at the External Storage documentation.
http://developer.android.com/guide/topics/data/data-storage.html#filesExternal
You're going to need to use Android methods to get correct sdcard directories, etc...
You can add it as another method of your own Activity if your code is small, or you can create a utility class, let's suppose
class MyUtilities {
public static final void copyfile(String srFile, String dtFile) throws IOException, FileNotFoundException{
File f1 = new File(srFile);
File f2 = new File(dtFile);
InputStream in = new FileInputStream(f1);
OutputStream out = new FileOutputStream(f2);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0){
out.write(buf, 0, len);
}
in.close();
out.close();
Log.d("MyUtilities", "File copied to " + f2.getAbsolutePath());
}
}
and you will use it as:
TextEdit text1 = findViewById(R.id.text1);
TextEdit text2 = findViewById(R.id.text2);
String file1 = text1.getText();
String file2 = text2.getText();
if (text1 != null and text2 != null) {
try{
MyUtilities.copyfile (file1, file2);
} catch(FileNotFoundException ex){
Log.e("MyUtilities", ex.getMessage() + " in the specified directory.");
} catch(IOException e){
Log.e("MyUtilities", e.getMessage());
}
}
I added logs instead of the System.out and changed the Exception mechanism to better match android needs.
I've read the Android SDK and I've found that the MediaRecorder class can take input from a Camera, Audio or other source and compress it. Through the setOutputFile method you can specify where you want the data to be stored (File or URI), but what if I want to store that data in a memory buffer and send it over a connection? Or process it before sending it? I mean is there a way not to create a file but to use a memory buffer only?
You can of course read the file in later and do whatever you want with it in the way of processing. Assuming that u holds the Uri to the resulting audio file, here is a code snippet that reads it into a byte array and then deletes the file.
String audioUri = u.getPath();
InputStream in = new BufferedInputStream(this.getContentResolver().openInputStream(u));
byte[] b = new byte[BUFSIZE];
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(mFileName/*mFilePath*/)));
int byteCnt = 0;
while (0 <= (byteCnt = in.read(b, 0, BUFSIZE)))
out.write(b, 0, byteCnt);
out.flush();
out.close();
// try to delete media file
try {
// Delete media file pointed to by Uri
new File(getRealPathFromURI(u)).delete();
} catch (Exception ex) {}
public String getRealPathFromURI(Uri contentUri) {
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}