Android - 将输入流存储到文件中

68

我正在从一个URL中获取XML feed并进行解析。我需要的是将其存储到手机内部,以便在没有互联网连接时可以解析保存的选项而不是实时更新。

我面临的问题是,我可以创建URL对象,使用getInputStream获取内容,但无法保存它。

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();
任何想法如何保存输入流以便稍后加载。谢谢。
10个回答

121

下面是代码,输入为您的inputStream。 然后在以后使用相同的文件(名称)和FileInputStream来读取数据。

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();
}

1
嵌套try块可行吗? - amitavk
2
异常可能随时发生,如果发生异常,系统资源将会泄漏。 - Volodymyr Lykhonis
"input" 变量是 InputStream 的一个实例,而不是 InputStreamReader。 - Hack06
我一直收到“文件为空”的异常。 - Fahad-Android

37

简单函数

尝试使用这个简单函数来整洁地包装它:

// 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();
        }
    }
}

感谢Jordan LaPrise及其回答


out.close() 应该在 finally {} 块中执行,以避免资源泄漏,如果发生异常。如果在此处执行 in.close(),则同样需要在 finally {} 块中执行,但关闭输入流的责任实际上应由调用方承担。 - jk7
@jk7 你说得很有道理。看一下更新后的答案吧。 - Joshua Pinter

28

Kotlin 版本(经过测试,无需第三方库):

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()
        }
    }
}
我们利用 use 函数,该函数会在结束时自动关闭两个流。
即使发生异常,流也会被正确地关闭。 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html
https://kotlinlang.org/docs/tutorials/kotlin-for-py/scoped-resource-usage.html

1
抱歉,如果出现异常(例如断开连接),我们可以将异常重新抛出到上一级吗? - CoolMind

8
一份简短的版本:
OutputStream out = new FileOutputStream(file);
fos.write(IOUtils.read(in));
out.close();
in.close();

14
因为它需要来自Apache Commons的IOUtils,这是一个重量级的依赖项,会增加移动应用程序的负担。大多数人更喜欢依赖于标准SDK类的解决方案。 - gMale
1
对于测试(testandroidTest构件),这是可以的,因为它不会被包含在应用程序中。 - Tim Kist

7

这里有一个解决方案,可以处理所有异常情况,并基于以前的答案:

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();
        }
    }
}

4
在你的应用程序的 build.gradle 文件中,在 dependencies 下添加以下内容:
    implementation 'commons-io:commons-io:2.5'

在你的代码中:
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"));


我已经添加了implementation 'commons-io:commons-io:2.5',并同步了,但是它仍然显示import org.apache.commons.io.FileUtils;中的FileUtils为红色? - CodeToLife

1

有IOUtils的方式:

copy(InputStream input, OutputStream output)

它的代码类似于这个:


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;
}

0

现代 Kotlin 方式

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)
        }
    }

0

你可以使用Google Guava

 import com.google.common.io.ByteStreams;

代码:

 try (FileOutputStream fos = new FileOutputStream(new File("C:\\example.txt"))){
      ByteStreams.copy(inputStream, fos)
 }

0
更加Kotlin风格的方法:
fun copyInputStreamToFile(inputStream: InputStream, file: File) {
    try {
        inputStream.use { input ->
            FileOutputStream(file).use { input.copyTo(it) }
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

我们使用use函数来处理inputStreamFileOutputStream。该函数确保在资源不再需要时正确关闭它们,即使出现异常情况也是如此。
此外,我们还利用了InputStream可用的copyTo扩展函数,它简化了复制过程并在内部处理了缓冲和循环逻辑。这使得代码更加简洁,并减少了与资源管理相关的错误的可能性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接