Files中的工厂

Files类中对InputStream, OutputStream以及Reader,Writer的操作封装了抽象工厂模式,抽象工厂是InputSupplier与OutputSupplier,具体工厂是Files中的newInputStreamSupplier(), newOutputStreamSupplier()等方法

而InputStream, OutputStream以及Reader,Writer则是抽象产品, 他们的各种实现和装饰器包装则为具体产品

Input与Output工厂

Files中将Input与Output(包括InputStream,OutputStream和Reader,Writer两大IO派系)的生成使用抽象工厂来实现,其中代表抽象工厂的两个类为

public interface InputSupplier<T> {

  T getInput() throws IOException;
} public interface InputSupplier<T> { T getInput() throws IOException;
}

在某些类的对Input和Output操作的工具方法则会使用这个Supplier作为形参,例如

CharStreams.newReaderSupplier(

com.google.common.io.InputSupplier<? extends java.io.InputStream> in,

java.nio.charset.Charset charset

)

这样调用CharStreams.newReaderSupplier()便可以通过传入不同的Supplier具体实现来达到多态和解耦

Files类中的具体工厂实现

Files中工厂的具体实现抽象为了一个方法,如下:

public static InputSupplier<FileInputStream> newInputStreamSupplier(
final File file) {
Preconditions.checkNotNull(file);
return new InputSupplier<FileInputStream>() {
@Override
public FileInputStream getInput() throws IOException {
return new FileInputStream(file);
}
};
} public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
File file) {
return newOutputStreamSupplier(file, false);
} public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
final File file, final boolean append) {
Preconditions.checkNotNull(file);
return new OutputSupplier<FileOutputStream>() {
@Override
public FileOutputStream getOutput() throws IOException {
return new FileOutputStream(file, append);
}
};
} public static InputSupplier<InputStreamReader> newReaderSupplier(File file,
Charset charset) {
return CharStreams.newReaderSupplier(newInputStreamSupplier(file), charset);
} public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
Charset charset) {
return newWriterSupplier(file, charset, false);
} public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
Charset charset) {
return newWriterSupplier(file, charset, false);
}

这些工厂返回的产品大致包括了FileInputStream, FileOutputStream, Reader, Writer

文件读取与写入函数

    /**
* 这里有意思的有些特殊文件是file.length() == 0,但是文件却有实际内容
* 所以不能直接开辟一个file.length()大小的byte[] buff来读取文件内容
* Guava的解决方法是通过一个buff[0x1000]大小的buff来逐步读取这个特殊的文件
* 将其写入到一个OutputStream以后再一次性将它out.toByteArray()并返回
*
* 而对于file.length() != 0 的情况,则是直接开辟一个byte[] buff[file.length()]大小的buff
* 一次性将file里的内容读取到buff中并返回,从而避免额外像读取上面说的特殊文件那样频繁的开辟小的byte[] buff
*/
public static byte[] toByteArray(File file) throws IOException {
Preconditions.checkArgument(file.length() <= Integer.MAX_VALUE);
if (file.length() == 0) {
// Some special files are length 0 but have content nonetheless.
return ByteStreams.toByteArray(newInputStreamSupplier(file));
} else {
// Avoid an extra allocation and copy.
byte[] b = new byte[(int) file.length()];
boolean threw = true;
InputStream in = new FileInputStream(file);
try {
ByteStreams.readFully(in, b);
threw = false;
} finally {
Closeables.close(in, threw);
}
return b;
}
} /**
* 通过toByteArray()方法将文件内容包装成字符串
*/
public static String toString(File file, Charset charset) throws IOException {
return new String(toByteArray(file), charset.name());
} /**
* 使用小buff byte[0x1000] 的方法来copy文件
* 因为直接使用InputStream不像File.length()那样可以直接获得长度
*/
public static void copy(InputSupplier<? extends InputStream> from, File to)
throws IOException {
ByteStreams.copy(from, newOutputStreamSupplier(to));
}

另外还有很多关于文件读取和写入函数的重载方法,实现方式大同小异,只是参数变化了一下

文件比较方法

/**
* 依旧是那个问题,有些特殊文件显示的文件长度为0,所以必须通过读取文件的byte内容去比较是否相等
*/
public static boolean equal(File file1, File file2) throws IOException {
if (file1 == file2 || file1.equals(file2)) {
return true;
} /*
* Some operating systems may return zero as the length for files
* denoting system-dependent entities such as devices or pipes, in
* which case we must fall back on comparing the bytes directly.
*/
long len1 = file1.length();
long len2 = file2.length();
if (len1 != 0 && len2 != 0 && len1 != len2) {
return false;
}
return ByteStreams.equal(newInputStreamSupplier(file1),
newInputStreamSupplier(file2));
}

临时目录的创建

    /**
* Atomically creates a new directory somewhere beneath the system's
* temporary directory (as defined by the {@code java.io.tmpdir} system
* property), and returns its name.
*
* <p>Use this method instead of {@link File#createTempFile(String, String)}
* when you wish to create a directory, not a regular file. A common pitfall
* is to call {@code createTempFile}, delete the file and create a
* directory in its place, but this leads a race condition which can be
* exploited to create security vulnerabilities, especially when executable
* files are to be written into the directory.
*
* <p>This method assumes that the temporary volume is writable, has free
* inodes and free blocks, and that it will not be called thousands of times
* per second.
*
* @return the newly-created directory
* @throws IllegalStateException if the directory could not be created
*/
public static File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"));
String baseName = System.currentTimeMillis() + "-"; for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException("Failed to create directory within "
+ TEMP_DIR_ATTEMPTS + " attempts (tried "
+ baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}

Guava Doc中说到,如果直接使用File.createTempFile()会有安全问题,先介绍一下createTempFile()

public static java.io.File createTempFile(java.lang.String prefix,
java.lang.String suffix,
java.io.File directory)
throws java.io.IOException

它的功能是创建临时文件,提供prefix和suffix以及创建的dir,文件名会使用prefix+random+suffix的形式构成,而中间的random随机数则是使LazyInitialization.random.nextLong()生成,最后使用递归的方式生成这个临时文件

createTempFile()方法的返回值是生成文件以后的抽象路径File对象

根据doc的提示,应该使用 deleteOnExit()方法来删除临时文件,deleteOnExit()的意思是在JVM停止时才删除这个文件.相当于缓存了删除命令,如果有多个文件有deleteOnExit(),他们在JVM停止时会逆序开始删除(最后调用deleteOnExit()方法的文件最先删除,类似于栈)

所谓安全问题, StackOverflow上有人提到了,加入使用createTempFile()来创建临时目录,会这么写

public static File createTempDirectory()
throws IOException
{
final File temp; temp = File.createTempFile("temp", Long.toString(System.nanoTime())); if(!(temp.delete()))
{
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
} if(!(temp.mkdir()))
{
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
} return (temp);
}

然后参见几位网友的讨论

按照Sarel Botha的说法,使用了 file.mkdir() 的返回值做判断后抛出异常是不会有问题的

而如果没有使用file.mkdir()的返回值做判断, 潜在的问题是在于Linux的tmp目录使用了sticky bit(只能删除用户自己创建的文件), 在多线程情况下会存在file.delete()和file.mkdir()操作的线程竞争问题.具体是什么我也没搞清楚

原帖地址: http://stackoverflow.com/questions/617414/create-a-temporary-directory-in-java

随意总而言之, 就是使用Guava提供的createTmpDir()更安全

未完待续 ...

Guava Files 源码分析(一)的更多相关文章

  1. Guava Files 源码分析(二)

    createTempDir()之后就没有什么有意思的函数了,基本上都是对Java IO函数操作的聚合,只看一个simplifyPath() /** * Returns the lexically cl ...

  2. Guava 源码分析(Cache 原理 对象引用、事件回调)

    前言 在上文「Guava 源码分析(Cache 原理)」中分析了 Guava Cache 的相关原理. 文末提到了回收机制.移除时间通知等内容,许多朋友也挺感兴趣,这次就这两个内容再来分析分析. 在开 ...

  3. Guava cacha 机制及源码分析

    1.ehcahce 什么时候用比较好:2.问题:当有个消息的key不在guava里面的话,如果大量的消息过来,会同时请求数据库吗?还是只有一个请求数据库,其他的等待第一个把数据从DB加载到Guava中 ...

  4. Guava 源码分析之Cache的实现原理

    Guava 源码分析之Cache的实现原理 前言 Google 出的 Guava 是 Java 核心增强的库,应用非常广泛. 我平时用的也挺频繁,这次就借助日常使用的 Cache 组件来看看 Goog ...

  5. 通过 spark.files 传入spark任务依赖的文件源码分析

    版本:spak2.3 相关源码:org.apache.spark.SparkContext 在创建spark任务时候,往往会指定一些依赖文件,通常我们可以在spark-submit脚本使用--file ...

  6. guava eventbus 原理+源码分析

    前言: guava提供的eventbus可以很方便的处理一对多的事件问题, 最近正好使用到了,做个小结,使用的demo网上已经很多了,不再赘述,本文主要是源码分析+使用注意点+新老版本eventbus ...

  7. Java源码分析:Guava之不可变集合ImmutableMap的源码分析

    一.案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样-- public static final Map<String,String& ...

  8. [Guava源码分析]ImmutableCollection:不可变集合

    摘要: 我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3888557.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的 ...

  9. [Guava源码分析]Ordering:排序

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3876466.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

随机推荐

  1. Ajax的text/plain、application/x-www-form-urlencoded和application/json

    Ajax的text/plain.application/x-www-form-urlencoded和application/json HTTP请求中,如果是get请求,那么表单参数以name=valu ...

  2. iconfont 在项目中的简单使用

    font-class引用 font-class是unicode使用方式的一种变种,主要是解决unicode书写不直观,语意不明确的问题. 与unicode使用方式相比,具有如下特点: 兼容性良好,支持 ...

  3. 牛客网 桂林电子科技大学第三届ACM程序设计竞赛 C.二元-K个二元组最小值和最大-优先队列+贪心(思维)

    链接:https://ac.nowcoder.com/acm/contest/558/C来源:牛客网 小猫在研究二元组. 小猫在研究最大值. 给定N个二元组(a1,b1),(a2,b2),…,(aN, ...

  4. Music in Car CF 746F

    题目:http://codeforces.com/problemset/problem/746/F 先感叹一下题目之长! 一些测试样例在后面给出. 题目大意: Sasha 去工作的路上喜欢听歌,途中经 ...

  5. 【基础知识】.Net基础加强 第二天

    第02天 .Net基础加强 1. 封装 1> 属性的封装: 属性封装字段:把变化封装一下,保留用户的使用方式 2> 把方法的多个参数封装成一个对象 3> 将一堆代码封装到一个方法中 ...

  6. python sys.argv[]的用法简明解释

    sys模块中文参考文档:http://xukaizijian.blog.163.com/blog/static/170433119201111625428624/ sys.argv[]: 「argv」 ...

  7. Acer宏碁暗影骑士3进阶版无法进入系统引导修复

    1.刚开机时,按住alt不放,不停点击F10进入恢复系统: 2.点击疑难解答,选择cmd: 3.以下是cmd命令,//注释不要复制 c: //进入C盘 cd windows cd system32 b ...

  8. JZYZOJ1140 飞船控制站

    http://172.20.6.3/Problem_Show.asp?id=1140 p1140 就一道非常普通的二分,但是非常蛋疼的是验证mid left的过程一直错(就是写一个k次循环然后根据可行 ...

  9. Shell 学习笔记之运算符

    基本运算符 算术运算符 val = expr 2 + 2 需要注意的是 表达式和运算符之间需要有空格(比如2 + 2,不能是2+2) 两边最外面的字符是`,在esc键下面,不是引号哦 乘号* 前面必须 ...

  10. vijos 1659 河蟹王国 线段树区间加、区间查询最大值

    河蟹王国 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 https://vijos.org/p/1659 Description 河蟹王国有一位河蟹国王,他 ...