I am facing a problem downloading PNG images from my server to my Android app. The problem is specific to PNG images (JPG works fine), and the issue is that the downloaded files are corrupt images. I will explain in more details, below.
Scenario :
I need to download JPG and PNG images from my server, and display them to the user of the Android app.
Issue :
The JPG images get downloaded without an issue. But the downloaded PNG files are corrupt. I have double checked the source of the images at my server, and they are proper. Its only the downloaded PNG files, that are corrupt. So, the problem probably lies in the way I am downloading them in Android.
Code Sample :
URL imageURL;
File imageFile = null;
InputStream is = null;
FileOutputStream fos = null;
byte[] b = new byte[1024];
try {
// get the input stream and pass to file output stream
imageURL = new URL(image.getServerPath());
imageFile = new File(context.getExternalFilesDir(null), image.getLocalPath());
fos = new FileOutputStream(imageFile);
// get the input stream and pass to file output stream
is = imageURL.openConnection().getInputStream();
// also tried but gave same results :
// is = imageURL.openStream();
while(is.read(b) != -1)
fos.write(b);
} catch (FileNotFoundException e) {
} catch (MalformedURLException e) {
} catch (IOException e) {
} finally {
// close the streams
try {
if(fos != null)
fos.close();
if(is != null)
is.close();
} catch(IOException e){
}
}
Any pointers on how I can work on this, will be very appreciated.
Note :
Since this is happening in a service, there are no problems of doing this inside an AsyncTask.
The problem is here
while(is.read(b) != -1)
fos.write(b);
This is wrong, because in each iteration it will write the full buffer (1024 bytes) to the file. But the previous read could have read less bytes than than (almost surely on the last loop, unless the image lenght happens to be exactly a multiple of 1024). You should check how many bytes were read each time, and write that amount of bytes.
int bytesRead;
while( (bytesRead = is.read(b)) != -1)
fos.write(b,0,bytesRead );
Your error makes you write always files with sizes that are multiple of 1024 - which of course is not the case in general. Now, what happens when a image is saved with extra trailing bytes depends on the format and on the image reader. In some cases, it might work. Still, it's wrong.
BTW: never swallow exceptions - even if that's not the problem today, it might be tomorrow and you might spend hours finding the problem.
Related
My Android app (Java 8, min SDK 24, target SDK 27) downloads images from an FTP server, using Apache's FTPClient (version 3.6).
There are a couple of smaller .jpg images (around 12kb each) that always download completely but all of the bigger .png images (8+ mb each) show up black in my app. The files on the server look fine, so I downloaded the images from the emulator:
Windows' "Photo" app displays a black, horizontal bar at the bottom of all the affected images and they are all missing a couple of bytes (according to Windows' file explorer).
Reading the downloaded image with BitmapFactory.decodeFile(....) on a real device returns a null Bitmap, even though the file exists.
My code (runs on a background thread):
public void downloadFiles(String remoteFolder, String localFolder, ArrayList<String> filenames) {
//login here
ftpClient.setConnectTimeout(10000);
ftpClient.setDefaultTimeout(10000);
OutputStream out = null;
try {
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
if(ftpClient.changeWorkingDirectory(remoteFolder)) {
for(String filename : filenames) {
FTPFile[] singleFile = ftpClient.listFiles(filename);
if(singleFile != null && singleFile.length > 0) { //check if file exists
String localPath = localFolder + File.separator + filename;
out = new FileOutputStream(localPath);
if(!ftpClient.retrieveFile(filename, out)) {
//Set error message here
out.close();
break;
}
out.close();
} else {
//Another error message here
break;
}
}
}
} catch (IOException e) {
//And another error message here
} finally {
try {
if(out!=null) { out.close(); }
} catch(IOException e) {
//Doesn't matter
}
}
//logout here
}
I tried out.flush() before closing the stream, a longer timeout (ftpClient.setDefaultTimeout(30000)) and even retrieveFileStream:
InputStream is = ftpClient.retrieveFileStream(filename);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buf = new byte[10000000];
int len;
while ((len = bis.read(buf)) > 0) {
out.write(buf, 0, len);
}
if(ftpClient.completePendingCommand()) {
Log.d(TAG,"Done"); //This is always printed, even though the files aren't complete
} else {
Log.d(TAG,"Not done");
}
out.close();
bis.close();
The problem persists. I added up len and and even ran bis.read a couple more times (if(fullLength<singleFile[0].getSize())) but no matter what, most of the time less than 10 bytes are missing in the downloaded file. Only rarely one of the images is actually downloaded completely but it seems to be pretty random.
How do I fix this, so it downloads all of the images completely?
I am trying to use the /system/bin/screencapture tool in Android in my program. I want to have the screenshot in a Bitmap object. (I know about the other methods, however, my program is using a SurfaceView and I cannot change that so none available on the internet I could find worked.)
I have found that using the -p option to encode it into a png file takes too much time. So I want to use the output without the -p option. However, I am unable to figure out what format that output uses. I have tried reading it into a byte array and using BitmapFactory.decodeByteArray() but that doesn't seem to work (method just returns null.)
TL;DR: What format does /system/bin/screencapture use when not using the -p option (or writing to a file name that ends with ".png")
Here's the relevant code:
Bitmap bitmap = null;
try {
Runtime.getRuntime().exec("/system/bin/screencap /storage/emulated/0/storage/screencap");
try {
Thread.sleep(45);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.v("findMe", "Finished writing file");
byte[] data = new byte[0];
FileInputStream fis = new FileInputStream("/system/bin/screencap");
while(fis.available() > 0){
data = append_to_byte_arr(data, (byte) fis.read());
}
Log.d("findMe", data.length + " is data.length");
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
} catch (IOException ioe) {
Log.e("findMe", "you failed", ioe);
return;
}
//...
private byte[] append_to_byte_arr(byte[] arr, byte item) {
byte[] temp = new byte[arr.length + 1];
System.arraycopy(arr, 0, temp, 0, arr.length);
temp[arr.length] = item;
return temp;
}
Thanks for the help.
Without the "-p" option the format depends about the filename extension: if it is ".png" the screenshot is automatically saved as PNG. Without any filename the screenshot is printed to "stdout". I tried many was and the best is to print out to "stdout" and then read directly the Bitmap pixels from there because the other methods involves the "screencap" to save to file and then your App should read it wasting "a lot of time" (near 1.5 seconds in both operations)
In my application the user can choose a file using the chooser Intent, which will then be "imported" into the application and saved in internal storage for security reasons. This all worked fine and still does on some devices, but for example on the Google Pixel on Android 7.1.1 it only functions normally for the first 4-6 files and afterwards it acts very odd.
The performance was going down drastically so I checked my storage usage and found that it was continuously growing, although the file I was supposed to be saving was less than 1mb large. Importing a file would cause the amount of storage taken by my app to rise past 500mb and upward. I can't seem to find the cause for this.
The method I am using to save the files which is called in an async background task:
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
OutputStream fos = new FileOutputStream(file);
int size = 0;
InputStream fis = getContentResolver().openInputStream(uri);
try{
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
byte[] buf = new byte[1024];
int len = 1024;
while((len = bis.read(buf,0,len)) != -1){
bos.write(buf,0,len);
size = size+1024;
Log.v("Bytes written",""+size);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(bis != null) bis.close();
if(bos != null) bos.close();
if(fis != null) fis.close();
if(fos != null) fos.close();
}catch(IOException e){
e.printStackTrace();
}
}
return Uri.fromFile(file);
The Uri which this function returns is then saved in an SQLite Database to be used later.
I appreciate all kinds of tips as to where this memory usage could be coming from.
Btw, this did not result from an update on the phone nor from any changes in my code, as it was working the last time I tested it and I haven't changed anything since.
I see a couple of things to correct:
1) The signature of the write method doesn't seem correct, if you write from a buffer you should use write(buff, offset, length).
2) You read into the buffer once, so it should be enugh to write out the buffer once too.
3) If you need to read the the buffer more than once, and write out the values more than once, use a while, not a do while. You have no garantee that the read operation was succesfull.
Ref to write method in Android Developer
I had an additional method which would append an index to a file added multiple times eg. "file.pdf", "file1.pdf","file2.pdf". This method wasn't correct, leading to an endless loop of creating a new file and appending an index. I managed to fix the problem by changing this method to avoid looping.
In retrospect I should have included that in my question.
i am able to get the path of the picture i want to copy, and able to get the path from where i want it to be copy, but still cant find the way to copy them.
any suggestion?
private void copyPictureToFolder(String picturePath, String folderName)
throws IOException {
Log.d("debug", folderName);
Log.d("debug", picturePath);
try {
FileInputStream fileInputStream = new FileInputStream(picturePath);
FileOutputStream fileOutputStream = new FileOutputStream(folderName+"/");
int bufferSize;
byte[] bufffer = new byte[512];
while ((bufferSize = fileInputStream.read(bufffer)) > 0) {
fileOutputStream.write(bufffer, 0, bufferSize);
}
fileInputStream.close();
fileOutputStream.close();
} catch (Exception e) {
Log.d("disaster","didnt work");
}
}
thanks.
You should use Commons-IO to copy a file, we are in 2013 ! No one wants do that manually. If you really want then you should consider a few things :
first a loop that copies your file, at every iteration you copy buffer.length bytes. In you current code, you don't loop and copy 512 bytes of source image into dest (whatever the source image size is).
take care of last iteration and only copy what you read
your try/catch structure is not correct, you should add a finally close to always close your source and destination file. Look here for an example : what is the exact order of execution for try, catch and finally?
With IOUtils, it will give something like
try {
IOUtils.copy( source, dest );
} finally {
IOUtils.closeQuietly( source );
IOUtils.closeQuietly( dest );
}
and don't catch anything, it will be forwarded to the caller.
I have 10 images and want to save all this images in android application memory . so whenever any use install this apps , he have already this all images .
i have hard coded many times and not get good response .
is there any way . so i will done it.
please help me
Store all your images in the assets folder of your APK. Once it gets installed, scan the internal memory to see if desired images are there. If not, copy them there. In this way, even when users clear data of your application, you can copy them back. Another good thing is the user will not know anything about it as well, so it a good user-experience. Only thing would be your APK size would increase, so manage accordingly.
Try the below piece of code
InputStream inputStream = getAssets().open("yourfile.jpg");
OutputStream out = new FileOutputStream(new File("/sdcard/yourfile.jpg"));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
inputStream.close();
out.flush();
out.close();
You can keep the files in your asset or res folder , here I have kept the file in res/drawable/ and copy them on sdcard when I require. In below code first we check if file doesn't exist the we create a bitmap from the drawable and write the file out to sdcard.
File file = new File(pathExt+"/Pictures/", "s1.png");
if(isSDCARDMounted()){
if (!file.exists()) {
bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.s1);
try {
FileOutputStream outStream = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
isSDCARDMounted : function for checking if card is mounted or not
pathExt : variable for external storage directory path
Make sure have permission set for writing on external storage
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />