68

I am retrieveing an XML feed from a url and then parsing it. What I need to do is also store that internally to the phone so that when there is no internet connection it can parse the saved option rather than the live one.

The problem I am facing is that I can create the url object, use getInputStream to get the contents, but it will not let me save it.

URL url = null;
InputStream inputStreamReader = null;
XmlPullParser xpp = null;

url = new URL("http://*********");
inputStreamReader = getInputStream(url);

ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File(getCacheDir(),"")+"cacheFileAppeal.srl"));

//--------------------------------------------------------
//This line is where it is erroring.
//--------------------------------------------------------
out.writeObject( inputStreamReader );
//--------------------------------------------------------
out.close();

Any ideas how I can go about saving the input stream so I can load it later.

Cheers

Vini.g.fer
  • 11,639
  • 16
  • 61
  • 90
Dobes
  • 683
  • 1
  • 5
  • 4

10 Answers10

120

Here it is, input is your inputStream. Then use same File (name) and FileInputStream to read the data in future.

try {
    File file = new File(getCacheDir(), "cacheFileAppeal.srl");
    try (OutputStream output = new FileOutputStream(file)) {
        byte[] buffer = new byte[4 * 1024]; // or other buffer size
        int read;

        while ((read = input.read(buffer)) != -1) {
            output.write(buffer, 0, read);
        }

        output.flush();
    }
} finally {
    input.close();
}
Yennefer
  • 5,704
  • 7
  • 31
  • 44
Volodymyr Lykhonis
  • 2,936
  • 2
  • 17
  • 13
37

Simple Function

Try this simple function to neatly wrap it up in:

// Copy an InputStream to a File.
//
private void copyInputStreamToFile(InputStream in, File file) {
    OutputStream out = null;

    try {
        out = new FileOutputStream(file);
        byte[] buf = new byte[1024];
        int len;
        while((len=in.read(buf))>0){
            out.write(buf,0,len);
        }
    } 
    catch (Exception e) {
        e.printStackTrace();
    } 
    finally {
        // Ensure that the InputStreams are closed even if there's an exception.
        try {
            if ( out != null ) {
                out.close();
            }

            // If you want to close the "in" InputStream yourself then remove this
            // from here but ensure that you close it yourself eventually.
            in.close();  
        }
        catch ( IOException e ) {
            e.printStackTrace();
        }
    }
}

Thanks to Jordan LaPrise and his answer.

Community
  • 1
  • 1
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
  • out.close() should be in a finally {} block to avoid leaking resources if an exception occurs. Same for in.close() if it performed here, though it is really the responsibility of the caller to close it. – jk7 Mar 16 '17 at 18:14
  • @jk7 You're probably right actually. Take a look at the updated answer. – Joshua Pinter Mar 17 '17 at 13:33
28

Kotlin version (tested and no library needed):

fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
    inputStream.use { input ->
        val outputStream = FileOutputStream(outputFile)
        outputStream.use { output ->
            val buffer = ByteArray(4 * 1024) // buffer size
            while (true) {
                val byteCount = input.read(buffer)
                if (byteCount < 0) break
                output.write(buffer, 0, byteCount)
            }
            output.flush()
        }
    }
}

We take advantage of use function which will automatically close both streams at the end.

The streams are closed down correctly even in case an exception occurs.

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html
https://kotlinlang.org/docs/tutorials/kotlin-for-py/scoped-resource-usage.html

vovahost
  • 34,185
  • 17
  • 113
  • 116
  • 1
    Sorry, what, if an exception oocurs (for instance, disconnection), can we rethrow the exception to an upper level? – CoolMind Jul 15 '19 at 12:00
7

A shorter version:

OutputStream out = new FileOutputStream(file);
fos.write(IOUtils.read(in));
out.close();
in.close();
Tuan Chau
  • 1,243
  • 1
  • 16
  • 30
  • 14
    because it requires IOUtils from Apache commons and that's a heavy weight dependency to add to a mobile app. Most people would rather a solution that relies on standard SDK classes. – gMale Feb 10 '17 at 15:59
  • 1
    For testing (`test` and `androidTest` artifacts), it's fine because it won't get included in app. – Tim Kist Nov 17 '17 at 11:46
7

Here is a solution which handles all the Exceptions and is based on the previous answers:

void writeStreamToFile(InputStream input, File file) {
    try {
        try (OutputStream output = new FileOutputStream(file)) {
            byte[] buffer = new byte[4 * 1024]; // or other buffer size
            int read;
            while ((read = input.read(buffer)) != -1) {
                output.write(buffer, 0, read);
            }
            output.flush();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
vovahost
  • 34,185
  • 17
  • 113
  • 116
4
  1. In your application's build.gradle file add under dependencies:
    implementation 'commons-io:commons-io:2.5'
  1. In your code:
import org.apache.commons.io.FileUtils;

// given you have a stream, e.g.
InputStream inputStream = getContext().getContentResolver().openInputStream(uri);

// you can now write it to a file with
FileUtils.copyToFile(inputStream, new File("myfile.txt"));

ccpizza
  • 28,968
  • 18
  • 162
  • 169
  • i put `implementation 'commons-io:commons-io:2.5` ,synced, but it shows `import org.apache.commons.io.FileUtils;`'s FileUtils in red? – CodeToLife Jun 02 '21 at 17:41
1

There's the way of IOUtils:

copy(InputStream input, OutputStream output)

The code of it is similar to this :

public static long copyStream(InputStream input, OutputStream output) throws IOException {
    long count = 0L;
    byte[] buffer = new byte[4096]; 
    for (int n; -1 != (n = input.read(buffer)); count += (long) n)
        output.write(buffer, 0, n);
    return count;
}
android developer
  • 114,585
  • 152
  • 739
  • 1,270
0

You can using Google Guava

 import com.google.common.io.ByteStreams;

Code:

 try (FileOutputStream fos = new FileOutputStream(new File("C:\\example.txt"))){
      ByteStreams.copy(inputStream, fos)
 }
拉拉姬
  • 140
  • 1
  • 8
0

Modern Kotlin way

fun File.copyInputStreamToFile(inputStream: InputStream?) {
    outputStream().use { fileOut ->
        inputStream?.copyTo(fileOut)
    }
}

// Sample of usage
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        data?.data?.let { uri ->
            val inputStream = contentResolver.openInputStream(uri)
            val file = File(cacheDir, "todo_filename.jpg")
            file.copyInputStreamToFile(inputStream)
        }
    }
Zakhar Rodionov
  • 1,398
  • 16
  • 18
0

A bit more Kotlin-y approach:

fun copyInputStreamToFile(inputStream: InputStream, file: File) {
    try {
        inputStream.use { input ->
            FileOutputStream(file).use { input.copyTo(it) }
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

We use the use function for both the inputStream and the FileOutputStream. The use function ensures that the resources are closed properly after they are no longer needed, even in case of exceptions.

Additionally, we utilize the copyTo extension function available for InputStream, which simplifies the copying process and handles the buffering and loop logic internally. This makes the code more concise and reduces the chances of errors related to resource management.

Sermilion
  • 1,920
  • 3
  • 35
  • 54