download from url with executor service - android

I used the code below in executor service to download a file from URL. It downloads the file but size is 0 bytes. code works fine when I use asyncTask. but when I put it in executor service doesn't work.
Any ideas why the download won't be complete?
onCreate:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ExecutorService pool = Executors.newSingleThreadExecutor();
Button btn = findViewById(R.id.btn);
String url = "https://file-examples-com.github.io/uploads/2017/10/file_example_JPG_100kB.jpg";
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
pool.execute(new NetworkService(url));
}
});
NetworkService class:
private class NetworkService implements Runnable {
private String url;
public NetworkService(String url) {
this.url = url;
}
#Override
public void run() {
dlFile(url);
}
private String dlFile(String surl) {
try {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(surl);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage();
input = connection.getInputStream();
String title = URLUtil.guessFileName(String.valueOf(url), null, null);
output = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/" + title);
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
} finally {
}
return null;
}
}

There are a couple of things you should do and verify.
1 - First of all, make sure you have granted the required permissions to save file to the storage.
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
INTERNET
2 - Add android:requestLegacyExternalStorage="true" to your manifest file's application tag for android 10 and later versions.
3 - Correct your FileOutputStream initialisation, because
val direct = File(Environment.getExternalStorageDirectory().toString() + "/test_files")
You are creating a path above in test_files Folder, but in FileOutputStream you have specified different path. So, the correct line should be
output = FileOutputStream( direct + "/" + title)
And finally, add the following line after output = line to write image to the buffer, because you are reading the InputStream but you are not writing it on the buffer. That is why, it only create the path in storage, but do not write file to that path.
val buf = ByteArray(1024)
var len: Int
while (input.read(buf).also { len = it } > 0) {
output.write(buf, 0, len)
}
PS: I have written the code in Kotlin and tested it on Android 10, It works fine.
I hope this helps. Please don't forget to accept the answer if it help.

Related

Loading typeface dynamically from url or statically from lib

I'm running an Android application and I want to load a font dynamically and use it during runtime. How can I do this?
And also how can I include a font in an SDK that I've written, reference the sdk in the app I've written, and use the font included in the SDK?
Edit: Thanks for putting a -1 Vote on this, whoever did this, I'll stop sharing knowledge, that's a good way to shut me down.
Here's how I would do it: (Using an AsyncTask, which is not perfect)
If you want something more stable than an AsyncTask RxAndroid offers other good variants, far more stable.
In this example I'm doing everything in the "doInBackground" section, but you can use it the same way, anywhere after the task is done.
This example also assumes we have persmissions to write and read from external storage.
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
public DownloadTask(Context context) {
this.context = context;
}
#Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// download the file
input = connection.getInputStream();
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File (sdCard.getAbsolutePath() + "/fonts");
dir.mkdirs();
File file = new File(dir, "font.ttf");
try {
OutputStream out = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while((len=input.read(buf))>0){
out.write(buf,0,len);
}
out.close();
input.close();
} catch (Exception e) {
e.printStackTrace();
}
File sdcard = Environment.getExternalStorageDirectory();
File dirs = new File(sdcard.getAbsolutePath()+"/fonts");
if(dirs.exists()) {
File[] files = dirs.listFiles();
Log.d("s","files");
}
final Typeface typeface = Typeface.createFromFile(
new File(Environment.getExternalStorageDirectory()+"/fonts", "font.ttf"));
Log.d("a","created");
// Now I'm starting with an example that shows how to use
// this font on a textview of my choice.
// Assumptions: font has characters uF102 and uF104
final TextView tv = (TextView) findViewById(R.id.myTextView);
runOnUiThread(new Runnable() {
#Override
public void run() {
if (tv != null && typeface != null) {
tv.setTypeface(typeface);
tv.setText("\uF102");
tv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (tv.getText().equals("\uF102")){
tv.setText("\uF104");
} else {
tv.setText("\uF102");
}
}
});
}
}
});
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
}
In case we want to load the font from an sdk we're using, of from a library we've written, we can include the font in the drawable raw section, and from the application using this sdk/lib we can reference the font like so:
(I've used the amaticobold font in this case just for example)
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File (sdCard.getAbsolutePath() + "/fonts");
dir.mkdirs();
File file = new File(dir, "font.ttf");
InputStream is = getResources().openRawResource(getResources().getIdentifier("amaticbold","raw", getPackageName()));
try {
OutputStream out = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while((len=is.read(buf))>0){
out.write(buf,0,len);
}
out.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
File sdcard = Environment.getExternalStorageDirectory();
File dirs = new File(sdcard.getAbsolutePath()+"/fonts");
if(dirs.exists()) {
File[] files = dirs.listFiles();
Log.d("s","files");
}
final Typeface typeface = Typeface.createFromFile(
new File(Environment.getExternalStorageDirectory()+"/fonts", "font.ttf"));
editText.setTypeface(typeface);

Android: Download from URL

i follow more explain in this site for download mp3 or picture from URL , I follow more method and try to write my method but when i run application it stop.
I make method to query download when click
also put permission for INTERNET & WRITE_EXTERNAL_STORAGE
put the problem is still
this method is download
public static void downloadMain(){
File fileToSave = null;
String scrPath ="http://***";
BufferedInputStream bis;
BufferedOutputStream bos;
fileToSave = new File(Environment.getExternalStorageDirectory().getPath() +"/"+ "A"+"/");
if (!fileToSave.exists())
fileToSave.mkdirs();
fileToSave = new File(Environment.getExternalStorageDirectory().getPath() +"/"+ "A" +"/" + "h"+"/");
if (!fileToSave.exists())
fileToSave.mkdirs();
File file = new File (fileToSave,"***.mp3");
try{
URL url = new URL(scrPath+"***.mp3");
URLConnection ucon = url.openConnection();
ucon.connect();
bis=new BufferedInputStream(ucon.getInputStream());
bos = new BufferedOutputStream(new FileOutputStream(file));
bis=new BufferedInputStream(url.openStream());
byte[] data = new byte[1024];
int a =0;
while(true){
int k = bis.read(data);
if(k==-1){
bis.close();
bos.flush();
bos.close();
break;
}
bos.write(data, 0, k);
a+=k;
}
}catch(IOException e){}
}
I have three main perplexity about your program:
Do you run the following code in an asynctask? (this must run asincronusly otherwise it will block)
Why it loop infinitly?
You couldn't open an url or a file named with a '*' inside of it
Edit:
You must run the download method asincronusly otherwise it wouldn't work, interaction with filesystem and network couldn't be done in the main thread
Edit2:
AsyncTask should be something like this
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
public DownloadTask(Context context) {
this.context = context;
}
#Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/file_name.extension");//put here your path and your mkdirs
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null)
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
else
Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
}
}
And you shoould call it like this
DownloadTask downloadTask = new DownloadTask(YourActivity.this);
downloadTask.execute("the url to the file you want to download");
You could also have a look at this answer

Dowloading a text file from web

I want to download a text file from a web url and save it locally on the device and use it in my app.
Code:
try {
File file = new File(getFilesDir(), "file.txt");
if (file.length() > 0) {
//File already exists and it is not empty
return;
}
URL url = new URL("https://www.abc.com/file.txt");
FileOutputStream fos = new FileOutputStream(file);
InputStream in = url.openStream();
byte[] buffer = new byte[1024];
int length = 0;
while ((length = in.read(buffer)) > 0) {
fos.write(buffer, 0, length);
}
fos.flush();
fos.close();
} catch (Exception e) {
// TODO:
}
As you can see, the code goes with getFilesDir() assuming that always exists. However there are few questions, with proper network connection and permissions:
Does my assumption of getFilesDir() fail in any case?
Are there any cases of either file not downloaded/wrong content etc.., with this code?
Once I faced an issue where the file is downloaded but has all encoded characters, no matter how may times I downloaded it, it still had the same encoded text. Only when I re-installer my app, then the proper text was downloaded. And never got that issue ever since. Any reason for that weird behavior?
EDIT:
Here is what I get as the content when I try to read the file which I downloaded(happens sometimes, 1 in 10) shown in the logcat:
Code to read the file:
BufferedReader inputReader= = new BufferedReader(
new InputStreamReader(new FileInputStream(file)));
String inputString;
StringBuffer stringBuffer = new StringBuffer();
while ((inputString = inputReader.readLine()) != null) {
Log.e("inputString: ", inputString);
}
inputReader.close();
Thank You
Does my assumption of getFilesDir() fail in any case?
According to the documentation it should always work with no permissions required.
Are there any cases of either file not downloaded/wrong content etc..,
with this code?
Sure, I mean just a simple connection drop will cause download failure and so many other things can go wrong like missing required permission (android.permission.INTERNET), wrong encoding, disk full, ...
Once I faced an issue where the file is downloaded but has all encoded
characters, no matter how may times I downloaded it, it still had the
same encoded text. Only when I re-installer my app, then the proper
text was downloaded. And never got that issue ever since. Any reason
for that weird behavior?
It might have been an encoding issue, wrap your FileOutputStream in an OutputStreamWriter, which allows you to pass encoding parameter in the constructor.
Writer writer = new OutputStreamWriter(fos);
.
.
.
writer.write(buffer, 0, length);
The following example may be helpful:
try {
// Create a URL for the desired page
URL url = new URL("mysite.com/thefile.txt");
// Read all the text returned by the server
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String str;
while ((str = in.readLine()) != null) {
// str is one line of text; readLine() strips the newline character(s)
}
in.close();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
That's not really an answer but an advice, use ion a networking library for Android.
From examples:
Ion.with(context)
.load("http://example.com/really-big-file.zip")
// have a ProgressBar get updated automatically with the percent
.progressBar(progressBar)
// and a ProgressDialog
.progressDialog(progressDialog)
// can also use a custom callback
.progress(new ProgressCallback() {#Override
public void onProgress(int downloaded, int total) {
System.out.println("" + downloaded + " / " + total);
}
})
.write(new File("/sdcard/really-big-file.zip"))
.setCallback(new FutureCallback<File>() {
#Override
public void onCompleted(Exception e, File file) {
// download done...
// do stuff with the File or error
}
});
All operations are done not in the UI thread, so the user always see a responsive app.
Try with below code:
public void downloadFile(){
String DownloadUrl = "Paste Url to download a text file hereā€¦";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(DownloadUrl));
request.setDescription("sample text file for testing"); //appears the same in Notification bar while downloading
request.setTitle("Sample.txt");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalFilesDir(getApplicationContext(),null, "sample.pdf");
// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
public static boolean isDownloadManagerAvailable(Context context) {
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
return false;
}
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName("com.android.providers.downloads.ui","com.android.providers.downloads.ui.DownloadList");
List <resolveinfo> list = context.getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
} catch (Exception e) {
return false;
}
}
I cannot really comment on what goes wrong in your case, I will post a snippet of a code I'm using to detect what type of file I'm targeting and then get it. This has always worked as expected for me. I've modified my "onPostExecute" method to suit my answer here and I've tried to keep the names of my variables similar to yours. I've omitted the download progress indication bar to simplify the snippet. The download has to be done in the background, therefore "AsyncTask" is used. For the snippet I use random text file from google.
final String file_url = "https://www.kernel.org/doc/Documentation/power/drivers-testing.txt";
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(file_url);
final String fileName = URLUtil.guessFileName(file_url, null, fileExtension);
final String path = Environment.getExternalStorageDirectory().toString() + "/" + fileName;
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
URL url = new URL(file_url);
URLConnection connection = url.openConnection();
connection.connect();
// download the file
InputStream in = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(path);
byte buffer[] = new byte[1024];
int length;
while ((length = in.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
output.flush();
output.close();
in.close();
} catch (IOException e) {
Log.e("Downloading file", "Download Error", e);
}
return null;
}
#Override
public void onPostExecute(Void result) {
try {
File file = new File(path);
BufferedReader inputReader = new BufferedReader(new FileReader(file));
String inputString;
while ((inputString = inputReader.readLine()) != null) {
Log.e("inputString: ", inputString);
}
inputReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.execute();

How to download file and store in sdcard?

I am working on the it should download the file from server and store in the sdcard .
But I getting the exception : java.io.filenotfoundexception (permission denied)
public class MainActivity extends ActionBarActivity {
// usually, subclasses of AsyncTask are declared inside the activity class.
// that way, you can easily modify the UI thread from here
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
File file,sdcard;
public DownloadTask(Context context) {
this.context = context;
}
#Override
protected String doInBackground(String... sUrl) {
System.out.print("BAckground");
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream(); '
GEtting exception in line '
output = new FileOutputStream("sdcard/file.mp3");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
Try this
File root = new File(Environment.getExternalStorageDirectory(), "file.mp3");
if (!root.exists()) {
root.mkdirs();
}
output = new FileOutputStream(root);
and add permission on manifest file as
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Try this code:
File yourFile = new File(Environment.getExternalStorageDirectory(),"yourfile.txt");
// replace yourfile.txt with "file.mp3" your file name
if(!yourFile.exists()) {
yourFile.createNewFile();
}
FileOutputStream output = new FileOutputStream(yourFile, false);
Also do not forget to add this permission in manifest file.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
You also need to check weather your sdcard is mounted or not,
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
// Mounted
}
else {
}
Do like this,
File root = new File(Environment.getExternalStorageDirectory(), "file.mp3");
if (!root.exists()) {
root.mkdirs();
}
output = new FileOutputStream(root);
And add this permission in Android menfestfile.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

How to retry in the async task downloader?

public class PreviewDownload extends AsyncTask<String, Void, String> {
public static final String TAG = "PreviewDownload";
public String inputPath = null;
public String outputFolder = null;
public IRIssue issue = null;
#Override
protected String doInBackground(String... parms) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
issue = Broker.model.issueDataStore.getIRIssue(parms[0]);
outputFolder = IRConstant.issueFolder(issue.year, issue.month, issue.day, issue.pubKey);
try {
inputPath = IRConstant.downloadFile(issue.year, issue.month, issue.day, issue.pubKey, "preview", "0");
URL url = new URL(inputPath);
Log.d (TAG,"input: " + inputPath);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
return null;
// return "Server returned HTTP " + connection.getResponseCode()
// + " " + connection.getResponseMessage();
// download the file
input = connection.getInputStream();
output = new FileOutputStream(outputFolder + "/preview.zip");
Log.d (TAG,"output: " + output);
byte data[] = new byte[1024];
int count;
while ((count = input.read(data)) != -1) {
output.write(data, 0, count);
}
} catch (Exception e) {
// return e.toString();
return null;
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return outputFolder;
}
#Override
protected void onPostExecute(String outputFolder) {
// TODO Auto-generated method stub
super.onPostExecute(outputFolder);
if (outputFolder != null) {
File zipFile = new File (outputFolder + "/preview.zip");
if (Utils.unzip(outputFolder,outputFolder + "/preview.zip" )) {
zipFile.delete();
issue.isThumbDownloaded = 1;
} else {
issue.isThumbDownloaded = 0;
}
} else {
Toast.makeText(Broker.launcherActivity.getBaseContext(), R.string.wordCantDownload, Toast.LENGTH_LONG).show();
issue.isThumbDownloaded = 0;
}
issue.updateProgress(issue.progress);
}
}
Here is the downloader I implemented , the problem is , when the network lost, the output become null and show error message, however, if I would like to retry two times before showing error message, are there any way to do this? If I perfer not to pass in an object instead of string ,is it not recommended? thanks
What prevents you from re-instanciating and re-executing a "Downloader" from your catch blocks in case of errors ?
You could use a single common shared object between dowloader instances to count the attempts, or better, pass a parameter to each of them. In the catch block, you would then retry if you didn't reach the limit, and increase the value passed to a new downloader... Something recursive.
int expectedLength = connection.getContentLength();
can you compare with the expectedLength & downloaded length and retry?

Categories

Resources