Blank PDF when save from okHttp - android

I have to save pdf file using bytes from http response.
If file is small (about hundreds of bytes, less than 1k) everything is ok, but if file is too large I saw (using another app via Intent) all pages white (number of pages is correct).
I save the document in external storage and pass uri to intent.
This is my code to get file from server and save it to external storage:
public static Observable<Integer> getDocument(final String id, final String service, final String filename){
return Observable.create(new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> subscriber) {
InputStream inputStream = null;
FileOutputStream outputStream = null;
OkHttpClient client = HypeHttpClientBuilder.getOkHttpClient(200); //timeout
RequestBody formBody = new FormEncodingBuilder()
.add("platform", HypeApplication.getDeviceType())
.add("function", service)
.add("pdfid",id)
.build();
Request requests = HttpRequestBuilder.getRequest(formBody);
try {
Response response = client.newCall(requests).execute();
inputStream = response.body().byteStream();
File dir = new File(Environment.getExternalStorageDirectory(),"Hype");
if (!dir.mkdirs()) {
Log.e(TAG, "Directory not created");
}
outputStream = new FileOutputStream(new File(dir, filename));
int totalCount = inputStream.available();
byte[] buffer = new byte[1024 * 1024];
int len;
int readLen = 0;
while ((len = inputStream.read(buffer)) != -1 ) {
//System.out.println("download loop " + Thread.currentThread().getName());
outputStream.write(buffer, 0, len);
readLen += len;
}
subscriber.onNext(readLen);
}catch (Exception e){
subscriber.onError(e);
}finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
subscriber.onCompleted();
}
});
}
and this is code to start another activity:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(FileProvider.getUriForFile(getActivity(),getActivity().getApplicationContext().getPackageName() + ".provider",f)
, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
UPDATE
I tried to use a proxy to intercept http call.
So, I got file from proxy and file generated by my code and I compared them: there are some differences.
I got a simple solution using UrlConnection class.
URL url = new URL(URLSetting.getOldUrl());
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("appversion","2.0.0");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
Uri.Builder builder = new Uri.Builder()
.appendQueryParameter("platform", HypeApplication.getDeviceType())
.appendQueryParameter("function", service)
.appendQueryParameter("deviceid",HypeApplication.getDeviceId())
.appendQueryParameter("pdfid", id);
OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(builder.build().getEncodedQuery());
writer.flush();
writer.close();
os.close();
conn.connect();
inputStream = conn.getInputStream();
So, do you know if okHttp set as default encoding UTF-8?

I'm not sure what exactly is causing your problem, but you could try simplifying your code by using Okio API.
BufferedSource source = response.body().source();
Sink out = Okio.sink(outputFile);
try {
while (!source.exhausted()) {
Buffer buffer = source.buffer();
out.write(buffer, buffer.size());
}
} finally {
out.close();
}
See if that changes anything.

Related

Download file within the app by clicking on the weblink.

I am developing app like playstore in which user can download any app. i have many apps in my application that i got from my website through wp api v2. when we click on any of the available application detail opened and it have a download link. when we click on the link it goes to the browser but what i want is when we click on any of the apps downloading link downloading should start within my app with progress bar. i didn't found any appropriate solution yet on stack or anywhere.
Here is the screenshot attached for better understanding. arrow is pointing to the downloading link.
Try this code, you can put this on click of the link(textview)
private static void downloadFile(String url, File outputFile) {
try {
URL u = new URL(url);
URLConnection conn = u.openConnection();
int contentLength = conn.getContentLength();
DataInputStream stream = new DataInputStream(u.openStream());
byte[] buffer = new byte[contentLength];
stream.readFully(buffer);
stream.close();
DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
fos.write(buffer);
fos.flush();
fos.close();
} catch(FileNotFoundException e) {
return; // swallow a 404
} catch (IOException e) {
return; // swallow a 404
}
}
you can use intent service to download the app.
Here is the code :
public class DownloadService extends IntentService {
File cacheDir;
public DownloadService() {
super("DownloadService");
}
#Override
public void onCreate() {
super.onCreate();
String tmpLocation =
Environment.getExternalStorageDirectory().getPath();
cacheDir = new File(tmpLocation);
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
}
#Override
protected void onHandleIntent(Intent intent) {
String remoteUrl = intent.getExtras().getString("url");
String location;
String filename =
remoteUrl.substring(
remoteUrl.lastIndexOf(File.separator) + 1);
File tmp = new File(cacheDir.getPath()
+ File.separator + filename);
if (tmp.exists()) {
location = tmp.getAbsolutePath();
stopSelf();
return;
}
try {
URL url = new URL(remoteUrl);
HttpURLConnection httpCon =
(HttpURLConnection) url.openConnection();
if (httpCon.getResponseCode() != 200)
throw new Exception("Failed to connect");
InputStream is = httpCon.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = is.read(buf))) {
out.write(buf, 0, n);
}
out.close();
is.close();
byte[] response = out.toByteArray();
FileOutputStream fos = new FileOutputStream(tmp);
fos.write(response);
fos.flush();
fos.close();
is.close();
location = tmp.getAbsolutePath();
} catch (Exception e) {
Log.e("Service", "Failed!", e);
}
}
}
Run this service with url passed in the intent

Android - Download and Open Pdf

I have a application that downloads and opens a pdf from a listview click listener.
A file downloads and save to my phone but it has a file size of 0 bytes therefore It can not open. I Used tutorial code from http://www.coderzheaven.com/2013/03/06/download-pdf-file-open-android-installed-pdf-reader/ Here is my code. Any help will be greatly appreciated.
public class OpenPdfActivity extends Activity {
TextView tv_loading;
String dest_file_path = "pdf-test.pdf";
int downloadedSize = 0, totalsize;
String download_file_url = "http://www.education.gov.yk.ca/pdf/pdf-test.pdf";
float per = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv_loading = new TextView(this);
setContentView(tv_loading);
tv_loading.setGravity(Gravity.CENTER);
tv_loading.setTypeface(null, Typeface.BOLD);
downloadAndOpenPDF();
}
void downloadAndOpenPDF() {
new Thread(new Runnable() {
public void run() {
Uri path = Uri.fromFile(downloadFile(download_file_url));
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(path, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
} catch (ActivityNotFoundException e) {
tv_loading
.setError("PDF Reader application is not installed in your device");
}
}
}).start();
}
File downloadFile(String dwnload_file_path) {
File file = null;
try {
URL url = new URL(dwnload_file_path);
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
// connect
urlConnection.connect();
// set the path where we want to save the file
File SDCardRoot = Environment.getExternalStorageDirectory();
// create a new file, to save the downloaded file
file = new File(SDCardRoot, dest_file_path);
FileOutputStream fileOutput = new FileOutputStream(file);
// Stream used for reading the data from the internet
InputStream inputStream = urlConnection.getInputStream();
// this is the total size of the file which we are
// downloading
totalsize = urlConnection.getContentLength();
setText("Starting PDF download...");
// create a buffer...
byte[] buffer = new byte[1024 * 1024];
int bufferLength = 0;
while ((bufferLength = inputStream.read(buffer)) > 0) {
fileOutput.write(buffer, 0, bufferLength);
downloadedSize += bufferLength;
per = ((float) downloadedSize / totalsize) * 100;
setText("Total PDF File size : "
+ (totalsize / 1024)
+ " KB\n\nDownloading PDF " + (int) per
+ "% complete");
}
// close the output stream when complete //
fileOutput.close();
setText("Download Complete. Open PDF Application installed in the device.");
} catch (final MalformedURLException e) {
setTextError("Some error occured. Press back and try again.",
Color.RED);
} catch (final IOException e) {
setTextError("Some error occured. Press back and try again.",
Color.RED);
} catch (final Exception e) {
setTextError(
"Failed to download image. Please check your internet connection.",
Color.RED);
}
return file;
}
void setTextError(final String message, final int color) {
runOnUiThread(new Runnable() {
public void run() {
tv_loading.setTextColor(color);
tv_loading.setText(message);
}
});
}
void setText(final String txt) {
runOnUiThread(new Runnable() {
public void run() {
tv_loading.setText(txt);
}
});
}
}
A couple of things:
Maybe add a timeout?
Try to NOT check the content length for now.
Use BufferedOutputStream
Something like this:
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream out = new BufferedOutputStream( fos);
try {
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout( 15000);
urlConnection.connect();
try {
InputStream in = urlConnection.getInputStream();
byte[] buffer = new byte[1024 * 1024];
int len = 0;
while (( len = in.read( buffer)) > 0)
{
out.write( buffer, 0, len);
}
out.flush();
} finally {
fos.getFD(). sync();
out.close();
}
} catch (IOException eio) {
Log.e("Download Tag", "Exception in download", eio);
}
(Including the logcat may help to have a better idea of what's going on)
One possible issue is this :
fileOutput.close();
don't ensure that all bytes are written to the disk. If you try to read the file immediately after closing the stream, it's possible that the file is not totally written yet (especially because your buffer is big).
The solution is to request a sync after the close():
fileOutput.close();
fileOutput.getFD().sync();
similar question here

How do i upload a file using HTTPUrlConnection urlConnection.setRequestMethod("PUT"); or HttpPut?

So, I can't get rid of this problem:
I need to upload a XML file and a .jpg file from my android app(API 8) to a HTTP server (win 2008 server with IIS 7.5). I've already enabled PUT verbs and uninstalled WebDav & webdav methods as suggested from previous searches.
Plus, i'm not sure i'm doing it right server side because i can't get any response back.
here's my code
URL fileurl = new URL("Server Upload Path");
HttpURLConnection urlConnection = (HttpURLConnection) fileurl
.openConnection();
urlConnection.setRequestMethod("PUT");
urlConnection.setDoOutput(true);
urlConnection.connect();
OutputStream os = urlConnection.getOutputStream();
File upFile = new File("My Local File");
//I'm sure the file exists
FileInputStream fis = new FileInputStream(upFile);
BufferedInputStream bfis = new BufferedInputStream(fis);
byte[] buffer = new byte[1024];
int bufferLength = 0;
// now, read through the input buffer and write the contents to the
// file
while ((bufferLength = bfis.read(buffer)) > 0) {
os.write(buffer, 0, bufferLength);
}
Sorry if I forget some info you may need to help. I'm new to android and IIS too.
Why not try a standard multi-part file upload request (based on POST and not PUT):
final static String MULTIPART_BOUNDARY = "------------------563i2ndDfv2rTHiSsdfsdbouNdArYfORhxcvxcvefj3q2f";
public static void sendFileToServer(String url, File logFiles) {
HttpURLConnection connection = null;
OutputStream os = null;
DataInputStream is = null;
try {
StringBuilder fullUrl = new StringBuilder(url);
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + MULTIPART_BOUNDARY);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("Connection", "Keep-Alive");
connection.connect();
os = new BufferedOutputStream(connection.getOutputStream());
if(os != null) {
os.write(("--" + MULTIPART_BOUNDARY + EOL).getBytes());
os.write(String.format("Content-Disposition:form-data;name=\"UploadedFile\";filename=\"%s\"\r\nContent-Type: application/x-zip-compressed\r\n\r\n", UPLOADED_FILE_NAME).getBytes());
// Upload file(s) data here and send
os.write((EOL + "--" + MULTIPART_BOUNDARY + "--" + EOL + EOL).getBytes());
os.flush();
os.close();
os = null;
}
if(connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// Process server response
}
} catch (MalformedURLException e) {
} catch (IOException e) {
} catch (Exception e1) {
} finally {
try {
if(os != null) {
os.close();
}
} catch (IOException e) {
Log.e(TAG, "sendFileToServer exception: close OutputStream", e);
}
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
Log.e(TAG, "sendFileToServer exception: close InputStream", e);
}
if(connection != null) {
connection.disconnect();
}
}
}

Android Download file- Jackson readvalue failing to load the java class - heap size error

I am trying to download an apk file through a rest service via http GET. The format recieved is JSON. I am able to recieve the data but it fails while deserializing to a java object. I get Outofmemory error.
I am running the file download process in an Asyn Task. Is there other better options to achieve the same functionality. The apk is about 2 MB. The data from a wcf service is Base64 encoded in json
private class DownloadFile extends AsyncTask<String, Integer, String>
{
#Override
protected String doInBackground(String... sUrl)
{
try
{
URL url = new URL(sUrl[0]);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setRequestProperty("Content-Type", "application/json");
c.setRequestProperty("Accept", "application/json");
c.setRequestProperty("Device","13");
c.setRequestProperty("Authorization","toughsecurity");
c.setDoInput(true);
c.connect();
String PATH = Environment.getExternalStorageDirectory() + "/download/";
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "app.apk");
FileOutputStream fos = new FileOutputStream(outputFile);
if(c.getResponseCode()==HttpURLConnection.HTTP_OK)
{
OutputStream out = new FileOutputStream(Environment.getExternalStorageDirectory() + "/download/"+"temp.txt");
int read = 0;
byte[] bytes = new byte[1024];
while ((read = c.getInputStream().read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
InputStream in=new FileInputStream(Environment.getExternalStorageDirectory() + "/download/"+"temp.txt");
ObjectMapper mapper=new ObjectMapper();
AppDownload download= mapper.readValue(in, AppDownload.class);
byte[] data1=Base64Coder.decodeLines(download.Data);
byte[] content = data1;
int size = content.length;
InputStream is1 = null;
byte[] b = new byte[size];
is1 = new ByteArrayInputStream(content);
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is1.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
}
c.disconnect();
}
catch (Exception e)
{
Log.e("Jackson error",e.getMessage());
}
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
// mProgressDialog.show();
}
#Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// mProgressDialog.setProgress(progress[0]);
}
}
i wouldn't be surprised if the JSON library is barfing on a 2MB JSON file.
i know it probably isn't an option to change the API, but it's pretty strange to be returning binary data in JSON. it's not made for that. the API should provide two endpoints ... one that gets the object meta data including a link to the content, and one that returns the content only in binary form.
you could also try another JSON library.

Downloading a file from HTTP connection which redirect to HTTPS connection

I am using Dropbox in my project to get tiny url from dropbox which is like http://www.db.tt/xyzabc.
When I try to download the file in HTC My touch my code works fine, but if I try in Motorola Atrix it throws exception unknown host db.tt.
Actually first I have url like http://www.db.tt/xyzabc which is HTTP url I open it than I get exception and in exception I get actual url to file which contain file and is HTTPS url in exception. I start downloading file here is my code which work for me:
public static void fileUrl(String fAddress, String localFileName,
String destinationDir) {
OutputStream outStream = null;
URLConnection uCon = null;
InputStream is = null;
try {
URL url;
byte[] buf;
int ByteRead, ByteWritten = 0;
url = new URL(fAddress);
outStream = new BufferedOutputStream(new FileOutputStream(
destinationDir + localFileName));
try {
// Here i have "http://www.db.tt/xyzabc"
// after i hit url i get exception and in exception that
// FileNotFoundException at https://www.dropbox.com/abcxyz
// i get actual actual url i parse that exception and
//retrive https://www.dropbox.com/xyzabc(actual url)
// but in motorolla atrix instead of that url i get
// unknownhost exception "db.tt"
uCon = url.openConnection();
// uCon.connect();
is = uCon.getInputStream();
} catch (Exception e) {
url = new URL(e.getMessage().substring(
e.getMessage().indexOf("https"),
e.getMessage().length()));
outStream = new BufferedOutputStream(new FileOutputStream(
destinationDir + localFileName));
uCon = url.openConnection();
is = uCon.getInputStream();
}
buf = new byte[size];
while ((ByteRead = is.read(buf)) != -1) {
outStream.write(buf, 0, ByteRead);
ByteWritten += ByteRead;
}
System.out.println("Downloaded Successfully.");
System.out.println("File name:\"" + localFileName
+ "\"\nNo ofbytes :" + ByteWritten);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
ok after few attempt i made it solve my self and here is the solution will be helpfull if someone got same problem it requires some error handling and modification according to need
After seeing class heirarchy of Connection i found that HttpsURLConnection is child of HttpURLConnection and HttpURLConnection is child of UrlConnection so i i used HTTPConnection instead of UrlConnection and as HttpsUrlConnection is concrete for HttpsUrlConnection it solved my problem
i continue iterating till i get Https url after redirect
public static void fileUrl(String fAddress, String localFileName,
String destinationDir) {
OutputStream outStream = null;
URLConnection uCon = null;
HttpURLConnection mHttpCon;
InputStream is = null;
try {
URL url;
byte[] buf;
int ByteRead, ByteWritten = 0;
url = new URL(fAddress);
outStream = new BufferedOutputStream(new FileOutputStream(
destinationDir + localFileName));
try {
mHttpCon = (HttpURLConnection) url.openConnection();
while (!url.toString().startsWith("https")) {
mHttpCon.getResponseCode();
url = mHttpCon.getURL();
mHttpCon = (HttpURLConnection) url.openConnection();
}
is = mHttpCon.getInputStream();
} catch (Exception e) {
e.printStackTrace();
// url = new URL(e.getMessage().substring(
// e.getMessage().indexOf("https"),
// e.getMessage().length()));
// outStream = new BufferedOutputStream(new FileOutputStream(
// destinationDir + localFileName));
//
// uCon = url.openConnection();
// is = uCon.getInputStream();
}
buf = new byte[size];
while ((ByteRead = is.read(buf)) != -1) {
outStream.write(buf, 0, ByteRead);
ByteWritten += ByteRead;
}
System.out.println("Downloaded Successfully.");
System.out.println("File name:\"" + localFileName
+ "\"\nNo ofbytes :" + ByteWritten);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void fileDownload(String fAddress, String destinationDir) {
int slashIndex = fAddress.lastIndexOf('/');
int periodIndex = fAddress.lastIndexOf('.');
String fileName = fAddress.substring(slashIndex + 1);
if (periodIndex >= 1 && slashIndex >= 0
&& slashIndex < fAddress.length() - 1) {
fileUrl(fAddress, fileName, destinationDir);
} else {
System.err.println("path or file name.");
}
}
This answer works - to an extent. I have a similar solution here
There is still a problem with Dropbox short hyperlinks on Atrix. They redirect from http to https but NOT to the required file, instead I get a whole lot of html from inside Dropbox.

Categories

Resources