java io流上传图片_Java IO流

一、基本概念

IO是Java对数据的操作是通过流的方式,数据传输是需要通道的,而IO流就是数据传输的通道。IO流可以形象的比喻为运送货物的传输带。IO流用来处理设备之间的数据传输,上传文件和下载文件,Java用于操作流的对象都在IO包中。总结一下,Java IO输入机制就是为了允许程序读取外部数据(包括来自于磁盘、光盘等存储设备的数据)和用户输入的数据。

Java的IO流通过java.io包下的类和接口来支持,在java.io包下主要包括输入、输出两种IO流,每种输入、输出流又可以分为字节流和字符流两大类,其中字节流以字节为单位来处理输入和输出操作,而字符流则以字符来处理输入和输出操作,除此以外,Java的IO流使用了装饰者设计模式,将IO流又分为底层节点流和上层的处理流,其中节点流用于和底层的物理存储节点直接关联——不同的物理节点获取节点流的方式可能存在一定的差异,但是程序可以把不同的物理节点流包装成统一的处理流,从而允许程序使用统一的输入和输出代码来读取不同的物理存储节点的资源。

二、IO流分类

2.1、根据操作的数据类型的不同可以分为 :字节流与字符流

字节流就是传输过程中,传输数据的最基本单位是字节的流,字节流的类通常以stream结尾;

字符流就是传输过程中,传输数据的最基本单位是字符的流。

其中1字符 = 2字节 、 1字节(byte) = 8位(bit) 、 一个汉字占两个字节长度。

字节流和字符流的区别:字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法;

读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节;

处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据;

2.2、根据数据的流向分为:输入流与输出流

程序(内存)作为参照物,程序从外部读取称为输入(Input),程序向外部写数据成为输出(Output)。

2.3、根据具体功能分为:节点流和处理流

节点流:以从或向一个特定的地方(节点)读写数据。如FileInputStream;

处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

常用的节点流如下:

1、文件: FileInputStream 、FileOutputStream、 FileReader和 FileWriter 文件进行处理的节点流;

2、字符串: StringReader 和StringWriter 对字符串进行处理的节点流;

3、数组: ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader和CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组);

4、管道: PipedInputStream 、PipedOutputStream 和PipedReaderPipedWriter对管道进行处理的节点流;

5、父类: InputStream、 OutputStream、 Reader和 Writer;

常用处理流如下:(关闭处理流会自动调用关闭里面节点流的方法)

1、缓冲流:BufferedInputStream、 BufferedOutputStream、 BufferedReader和BufferedWriter   增加缓冲功能,避免频繁读写硬盘。

2、转换流:InputStreamReader和 OutputStreamReader实现字节流和字符流之间的转换。

3、数据流 DataInputStream和DataOutputStream等提供将基础数据类型写入到文件中,或者读取出来。

流关闭的原则:先打开先关闭;如果A依赖B,则先关闭A再关闭B;对于处理流如果将节点流关闭以后再关闭处理流,会抛出IO异常,所以直接关闭处理流就行了,会自动调用关闭里面节点流的方法。

注意:1、如果将节点流关闭以后再关闭处理流,会抛出IO异常;2、如果关闭了处理流,在关闭与之相关的节点流,也可能出现IO异常。

三、IO流体系

通常来讲,字节流的功能比字符流的功能强大,因为计算机里所有的数据都是二进制的,而字节流可以处理所有的二进制文件——但是问题是,如果使用字节流来处理文本文件(能用记事本打开并能看到其中的字符内容的是文本文件,反之是二进制文件),则需要使用合适的方式把这些字节转化成字符,这就增加了编程的复杂度,所以通常有一个规则:如果进行输入/输出的内容是文本内容,则应该考虑使用字符流,如果进行输入/输出的内容是二进制内容,则应该考虑使用字节流。

所有的流都继承InputStream、outputStream、Reader和Writer4个基本的流抽象类:

3.1、字节输入流

ByteArrayInputStream:字节数组输入流,它的内部缓冲区就是一个字节数组,该类的功能就是从字节数组(byte[])中进行以字节为单位的读取资源文件;

PipedInputStream:管道字节输入流,它和PipedOutputStream一起使用,能实现多线程间的管道通信。多线程管道通信的主要流程是在一个线程中向PipedOutputStream写入数据,这些数据会自动传送到对应的管道输入流PipedInputStream中,其他线程通过读取PipeInputStream中缓冲的数据实现多线程间通信;

FilterInputStream :过滤输入流,装饰者模式中处于装饰者,具体的装饰者都要继承它,所以在该类的子类下都是用来装饰别的流的,也就是处理类。常见的子类有DataInputStream和BufferedInputStream;

BufferedInputStream:缓冲输入流,由于基础输入流一个字节一个字节读取,频繁与磁盘进行交互,造成读取速度较低.缓冲流的存在就是先将数据读取到缓冲流(内存中),然后一次性从内存中读取多个字符.提高读取的效率;

DataInputStream:数据输入流,以机器无关的方式读取Java的基本类型;

FileInputSream:文件输入流,它通常用于对文件进行读取操作;

File:对指定目录的文件进行操作。注意,该类虽然是在IO包下,但是并不继承自四大基础类;

ObjectInputStream:对象输入流,用来提供对“基本数据或对象”的持久存储。通俗点讲,也就是能直接传输对象(反序列化中使用)。

3.2、字节输出流

同InputStream相对应的功能,如下分析:

ByteArrayOutputStream:字节数组输出流,它的内部缓冲区就是一个字节数组,该类的功能就是从字节数组(byte[])中进行以字节为单位的写入资源文件;

PipedOutputStream :管道字节输出流,它和PipedInputStream一起使用,能实现多线程间的管道通信。

FilterOutputStream :过滤输出流,装饰者模式中处于装饰者,具体的装饰者都要继承它,所以在该类的子类下都是用来装饰别的流的,也就是处理类。常见的子类有DatOutputStream、BufferedOutputStream和

BufferedOutputStream:缓冲输出流,由于基础输入流一个字节一个字节写入,频繁与磁盘进行交互,造成读取速度较低.缓冲流的存在就是先将数据写入到缓冲流(内存中),然后一次性从内存中写入多个字符.提高读取的效率;

DataOutputStream:数据输出流,以机器无关的方式读取Java的基本类型;

PrintStream:继承了FilterOutputStream。是"装饰类"的一种,所以属于字节流体系中(与PrintStream相似的流PrintWriter继承于Writer,属于字符流体系中),为其他的输出流添加功能.使它们能够方便打印各种数据值的表示形式;

FileOutputStream :文件输出流,它通常用于对文件进行写入操作;

ObjectOutputStream :对象输出流,用来提供对“基本数据或对象”的持久存储。通俗点讲,也就是能直接传输对象(反序列化中使用),和所有FilterOutputStream 的子类都是装饰流(序列化中使用)。

3.3、字符输入流

CharArrayReader :字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流

PipedReader:管道字符流, 是从与其它线程共用的管道中读取数据。

FilterReader:过滤输入字符流, 是所有自定义具体装饰流的父类,为所有装饰类提供一个标准、只是简单重写了父类Reader的所有方法、要求子类必须重写核心方法、和提供具有自己特色的方法、这里没有像字节流那样有很多的子类来实现不同的功能、可能是因为字符流本来就是字节流的一种装饰、所以在这里没有必要再对其进行装饰、只是提供一个扩展的接口而已;

BufferedReader:缓冲字符流, 为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率;

InputStreamReader是一个连接字节流和字符流的桥梁,它将字节流转变为字符流;

FileReader:继承InputStreamReader,可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系。

3.4、字符输出流

CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。

PipedWriter 是向与其它线程共用的管道中写入数据 BufferedWriter 是一个装饰器为Writer 提供缓冲功能。

PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。

OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似。

四、转化流

4.1、定义:字符和字节直接的转换,是字符流和字节流之间的桥梁,文本文件在硬盘中以字节流的形式存储时,通过InputStreamReader读取后转化为字符流给程序处理,即可对读取到的字节数据经过指定编码转换成字符;程序处理的字符流通过OutputStreamWriter转换为字节流保存,即可对读取到的字符数据经过指定编码转换成字节。

4.2、何时使用转换流?

①当字节和字符之间有转换动作时; ②流操作的数据需要编码或解码时。

4.3、具体的对象体现:

InputStreamReader:字节到字符的桥梁 OutputStreamWriter:字符到字节的桥梁 这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来,即:

InputStreamReader(InputStream in):将字节流以字符流输入。

OutputStreamWriter(OutStreamout):将字节流以字符流输出。

五、IO流的选择

1、首先考虑是选择输入流还是输出流,如果想从程序写东西到别的地方,那么就选择输出流,反之用输入流;

2、然后考虑你传输数据时,是选择使用字节流传输还是字符流,也就是每次传1个字节还是1个字符(2个字节),有中文肯定就选择字符流了;

(字符流和字节流的使用范围:字节流一般用来处理图像,视频,以及PPT,Word类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,字节流可以用来处理纯文本文件,但是字符流不能用于处理图像视频等非文本类型的文件)

结论:只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。

3、前面两步就可以选出一个合适的节点流了,比如字节输入流InputStream,如果要在此基础上增强功能,就在处理流中选择一个合适的即可。

六、IO流特性

1、先进先出,最先写入输出流的数据最先被输入流读取到;

2、顺序存取,可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile可以从文件的任意位置进行存取(输入输出)操作);

3、只读或只写,每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

七、IO流底层分析File(文件特征与管理):java.io.File代表与平台无关的文件或目录。也就是说可以通过File类在Java程序中操作文件或目录;

File类只能用来操作文件或目录(包括新建、删除、重命名文件和目录等操作),但不能用来访问文件中的内容;

如果需要访问文件中的内容,则需要使用输入/输出流。

如上图所示,java.io.File类是文件或路径的抽象表达,它实现了Serializable和Comparable接口,所以支持File对象的持久化,以及文件之间的大小比较。

2.InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。

/*** 该抽象类是所有字节输入流的超类*/

public abstract class InputStream implements Closeable {

// 该变量用于确定在skip方法中使用的最大缓存数组大小。 private static final int MAX_SKIP_BUFFER_SIZE = 2048;

//抽象方法,必须由子类实现 public abstract int read() throws IOException;

/*** 每次读取都只会读取一个字节,一个byte出来,是有顺序的,读完这个字节,会自动跳到下一个字节等待读取* 注意返回值的定义是int,不是byte:如果有byte可读,就返回这个byte对应的无符号类型的值(0 - 255)* @param b 缓存所读取的数据* @return 返回值可以理解成某个“字符”的ASCII码值(如读取到A了,那么就会返回65);如果没有byte可读就直接返回-1* @throws IOException*/

public int read(byte b[]) throws IOException {

return read(b, 0, b.length);

}

/*** 循环调用read()方法,将读取到的int转换成byte,然后再放到参数b的特定位置(从off开始放,放len个,* 当然可能放不满len个,是等待读取的字节数而定);注意子类可能会重写这个方法,会有另外的实现* @param b 缓存所读取的数据* @param off 将读取的数据写入数组b的起始偏移地址* @param len 读取的最大字节数组* @return 返回值是实际读出来的字节数,如果没有则返回-1* @throws IOException*/

public int read(byte b[], int off, int len) throws IOException {

if (b == null) {

throw new NullPointerException();

} else if (off < 0 || len < 0 || len > b.length - off) {

throw new IndexOutOfBoundsException();

} else if (len == 0) {

return 0;

}

int c = read();

if (c == -1) {

return -1;

}

b[off] = (byte)c;

int i = 1;

try {

for (; i < len ; i++) {

c = read();

if (c == -1) {

break;

}

b[off + i] = (byte)c;

}

} catch (IOException ee) {

}

return i;

}

//其实现是将待跳过的部分读取出来而不使用 public long skip(long n) throws IOException {

long remaining = n;

int nr;

if (n <= 0) {

return 0;

}

int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);

byte[] skipBuffer = new byte[size];

while (remaining > 0) {

nr = read(skipBuffer, 0, (int)Math.min(size, remaining));

if (nr < 0) {

break;

}

remaining -= nr;

}

return n - remaining;

}

public int available() throws IOException {

return 0;

}

//关闭输入流并释放与其相关的系统资源 public void close() throws IOException {}

//标记输入流中当前的位置。当调用reset方法可以回到上次标记的位置,使得后面可以重新读取相同的字节。 public synchronized void mark(int readlimit) {}

//将流重定位到最后一次对此输入流调用mark方法时的位置。 public synchronized void reset() throws IOException {

throw new IOException("mark/reset not supported");

}

//测试输入流是否支持test和reset方法。所支持的mark和reset是否是输入流实例的不变属性 //InputStream的markSupported方法返回false。 public boolean markSupported() {

return false;

}

}

3.OutputStream(二进制格式操作):抽象类,基于字节的输出操作,是所有输出流的父类。定义了所有输出流都具有的共同特征。

/*** 它是抽象类,并且实现了两个接口Closeable和Flushable。*/

public abstract class OutputStream implements Closeable, Flushable {

/*** @param b 作为要写入的数据,是int类型不是byte类型;因为int是占4个字节共32位,这里规定的是取低8位,忽略高24位,* 所以最后写入的是一个字节;参数可以理解成某个ASCII码值的十进制,* 写入的就是对应的“字符”(如b=65时,写入的就是A)* @throws IOException*/

public abstract void write(int b) throws IOException;

//此方法直接输出一个字节数组中的全部内容,调用了下面的write方法 public void write(byte b[]) throws IOException {

write(b, 0, b.length);

}

//同InputStream,循环调用write()方法,将b[off] 到 b[off + len - 1] 的值写入到目的数据源 public void write(byte b[], int off, int len) throws IOException {

if (b == null) {

throw new NullPointerException();

} else if ((off < 0) || (off > b.length) || (len < 0) ||

((off + len) > b.length) || ((off + len) < 0)) {

throw new IndexOutOfBoundsException();

} else if (len == 0) {

return;

}

for (int i = 0 ; i < len ; i++) {

write(b[off + i]);

}

}

//刷新此输出流并强制写出所有缓冲的输出字节。 //flush 的常规协定是:如果此输出流的实现已经缓冲了以前写入的任何字节,则调用此方法指示应将这些字节立即写入它们预期的目标。 public void flush() throws IOException {

}

//关闭此输出流并释放与此流有关的所有系统资源。 // close 的常规协定是:该方法关闭输出流,关闭的流不能执行输出操作,也不能重新打开。 public void close() throws IOException {

}

}

4.Reader(文件格式操作):抽象类,基于字符的输入操作。

public abstract class Reader implements Readable, Closeable {

/***用来在流上同步操作的对象。为了提高效率,字符流对象可以使用其自身以外的对象来保护关键部分。*因此,子类应使用此字段中的对象,而不是 this 或者同步的方法。*/

protected Object lock;

//构造方法1,使用本类类型"锁对象" protected Reader() {

this.lock = this;

}

/***构造方法2,使用指定类型的"锁对象"*/

protected Reader(Object lock) {

if (lock == null) {

throw new NullPointerException();

}

this.lock = lock;

}

/***试图将字符读入指定的字符缓冲区。缓冲区可照原样用作字符的存储库:所做的唯一改变是 put操作的结果。*不对缓冲区执行翻转或重绕操作。*/

public int read(java.nio.CharBuffer target) throws IOException {

int len = target.remaining();

char[] cbuf = new char[len];

int n = read(cbuf, 0, len);

if (n > 0)

target.put(cbuf, 0, n);

return n;

}

//读取单个字符,这里的重载read方法都基于read(char[], int, int) public int read() throws IOException {

char cb[] = new char[1];

if (read(cb, 0, 1) == -1)

return -1;

else

return cb[0];

}

//将字符读入数组中 public int read(char cbuf[]) throws IOException {

return read(cbuf, 0, cbuf.length);

}

//将字符读入数组的某一部分 abstract public int read(char cbuf[], int off, int len) throws IOException;

//当执行skip时,表示能跳过的最大的字节个数 private static final int maxSkipBufferSize = 8192;

private char skipBuffer[] = null;

//跳过字符 public long skip(long n) throws IOException {

if (n < 0L)

throw new IllegalArgumentException("skip value is negative");

int nn = (int) Math.min(n, maxSkipBufferSize);

synchronized (lock) {

if ((skipBuffer == null) || (skipBuffer.length < nn))

skipBuffer = new char[nn];

long r = n;

while (r > 0) {

int nc = read(skipBuffer, 0, (int)Math.min(r, nn));

if (nc == -1)

break;

r -= nc;

}

return n - r;

}

}

//判断该字符流是否已经准备好读取。 public boolean ready() throws IOException {

return false;

}

//判断此流是否支持 mark() 操作。 public boolean markSupported() {

return false;

}

//标记流中的当前位置 public void mark(int readAheadLimit) throws IOException {

throw new IOException("mark() not supported");

}

//重置该流。如果已标记该流,则尝试在该标记处重新定位该流。 public void reset() throws IOException {

throw new IOException("reset() not supported");

}

// 关闭该流并释放与之关联的所有资源 abstract public void close() throws IOException;

}

5.Writer(文件格式操作):抽象类,基于字符的输出操作。

package java.io;

/*** Writer是写入字符流的抽象类。定义了流的最基本的功能。* 子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。* 但是,多数子类将重写此处定义的一些方法,以提供更高的效率或其他功能。*/

public abstract class Writer implements Appendable, Closeable, Flushable {

//字符buffer private char[] writeBuffer;

//字符buffer的默认大小 private static final int WRITE_BUFFER_SIZE = 1024;

//用于同步针对此流的操作的对象 protected Object lock;

//构造方法1,使用本类对象锁 protected Writer() {

this.lock = this;

}

//构造方法2,使用指定的锁对象 protected Writer(Object lock) {

if (lock == null) {

throw new NullPointerException();

}

this.lock = lock;

}

/*** 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。* 用于支持高效单字符输出的子类应重写此方法。*/

public void write(int c) throws IOException {

synchronized (lock) { // write()方法内是同步代码块 if (writeBuffer == null){

writeBuffer = new char[WRITE_BUFFER_SIZE];

}

writeBuffer[0] = (char) c;

write(writeBuffer, 0, 1);

}

}

//写入字符数组 public void write(char cbuf[]) throws IOException {

write(cbuf, 0, cbuf.length);

}

//写入字符数组的某一部分。子类需实现该方法 abstract public void write(char cbuf[], int off, int len) throws IOException;

//写入字符串 public void write(String str) throws IOException {

write(str, 0, str.length());

}

//写入字符串的某一部分 public void write(String str, int off, int len) throws IOException {

synchronized (lock) {

char cbuf[];

if (len <= WRITE_BUFFER_SIZE) {

if (writeBuffer == null) {

writeBuffer = new char[WRITE_BUFFER_SIZE];

}

cbuf = writeBuffer;

} else { // Don't permanently allocate very large buffers. cbuf = new char[len];

}

str.getChars(off, (off + len), cbuf, 0);

write(cbuf, 0, len);

}

}

//将指定字符序列添加到此 writer public Writer append(CharSequence csq) throws IOException {

if (csq == null)

write("null");

else

write(csq.toString());

return this;

}

//将指定字符序列的子序列添加到此 writer public Writer append(CharSequence csq, int start, int end) throws IOException {

CharSequence cs = (csq == null ? "null" : csq);

write(cs.subSequence(start, end).toString());

return this;

}

//将指定字符添加到此 writer public Writer append(char c) throws IOException {

write(c);

return this;

}

//刷新该流的缓冲 abstract public void flush() throws IOException;

/*** 关闭此流,但要先刷新它。* 在关闭该流之后,再调用 write() 或 flush() 将导致抛出 IOException。*/

abstract public void close() throws IOException;

}

6、RandomAccessFile(随机文件操作):一个独立的类,直接继承至Object.它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。

RandomAccessFile类支持“随机访问”方式,这里“随机”是指可以跳转到文件的任意位置处读写数据。在访问一个文件的时候,不必把文件从头读到尾,而是希望像访问一个数据库一样“随心所欲”地访问一个文件的某个部分,这时使用RandomAccessFile类就是最佳选择。

RandomAccessFile对象类有个位置指示器,指向当前读写处的位置,当前读写n个字节后,文件指示器将指向这n个字节后面的下一个字节处。刚打开文件时,文件指示器指向文件的开头处,可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。 RandomAccessFile类在数据等长记录格式文件的随机(相对顺序而言)读取时有很大的优势,但该类仅限于操作文件,不能访问其他的I/O设备,如网络、内存映像等。

八、总结:

InputStream类的功能不足被Scanner解决了

OutputStream类的功能不足被PrintStream解决了

Reader类功能不足被BufferReader解决了

Writer类的功能不足被PrintWriter解决了

输出数据用printStream,printwriter读取数据用Scanner其次是bufferReader。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值