(1)InputStream流(输入流)-----对应读read
class MyClass implements Closeable {
@Override
public void close() throws IOException {
System.out.println("我被关闭了......");
}
}
public class InputStreamDemo {
public static void main(String[] args) {
}
private static void demo6() throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream("src/info.txt");
try (inputStream) {
byte[] bytes = new byte[5];
//public int read(byte b[], int off, int len) //read带三个参数的
//off:指定的索引位置存储字节内容
//len: 存储字节个数
int result = inputStream.read(bytes, 0, bytes.length);
System.out.println(Arrays.toString(bytes));
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void demo5() throws FileNotFoundException {
//对于输入流而言: read 文件必须要存在的
FileInputStream inputStream = new FileInputStream("src\\com\\javasm\\io\\InputStreamDemo.java");
try (inputStream) {
//读取数据
byte[] bytes = new byte[1024]; //一般都是1024的整数倍,理论上来说,空间越大,速度越快
//int result = inputStream.read(bytes); //read参数为字节数组
//一次读取1024个字节内容 读取到的字节的数据都存储到了bytes数组中
//result: 维护读取到的有效的字节个数
//有效的字节的数据是多少个? 不一定是length
//System.out.println(result);
//System.out.println(Arrays.toString(bytes));
//1.循环遍历字节数组 将每一个字节转换成char result次
//2.将字节数组转换成字符串的数据(看不懂的数据转换成看的懂的字符串)
//String s = new String(bytes, 0, result);
//System.out.println(s);
//循环读取指定的文件的数据
int len;
while ((len = inputStream.read(bytes)) != -1) { //还未读到文件末尾
System.out.print(new String(bytes, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void demo4() throws FileNotFoundException { //抛异常
InputStream inputStream = new FileInputStream("src/info.txt");
MyClass myClass = new MyClass();
try (inputStream; myClass;) { //1.7+之后,可在小括号里加多个需要释放资源的对象
inputStream.read();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void demo3() {
//1.7+ 提供了一种更加优雅的方式"释放流对象",会自动调用close
//try....with...resources 资源释放就不需要编写了 不需要finally
try (
//try()里放的是需要释放资源的对象,要求这些对象必须实现Closeable
InputStream inputStream = new FileInputStream("src/info.txt");
MyClass myClass = new MyClass(); //也实现了Closeable接口
) {
System.out.println(inputStream.read());
} catch (IOException e) {
e.printStackTrace();
} //不再需要finally释放资源,已自动调用close
//如何证明close被自动关闭 让MyClass实现Closeable接口,并重写close方法。第一次运行try里不加MyClass myClass = new MyClass();
//的代码,然后在try里加该代码,若真的关闭了,自然会走MyClass重写的方法
}
private static void demo2() {
InputStream inputStream = null;
try {
//1.创建字节输入流对象
inputStream = new FileInputStream("src/info.txt");
//文件不存在,无法读取
System.out.println(inputStream.read());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null)
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void demo1() {
//读取指定文件里面的数据----> 不要操作中文 read(读)----inputStream输入流
String filePath = "src/info.txt";
//读取
InputStream inputStream = null;
try {
//1.创建字节输入流对象 并获得文件字节数据
inputStream = new FileInputStream(filePath); //多态创建
//2.读取流里面字节数据
/*int data = inputStream.read(); //无参的,一次读一个字节,返一个字节内容 读完所有的字节数据,读到文件末尾,返-1
//System.out.println("读取到的第一个字节:" + (char)data);
//循环去读
while (data != -1) {
System.out.print((char) data);
data = inputStream.read();
}*/
int result;
while ((result = inputStream.read()) != -1) {
System.out.print((char) result);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放资源
try {
if (inputStream != null) inputStream.close(); //不为空关闭
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)OutputStream流(输出流)-----对应写write
只要调close,就会提前flush()。
public class OutputStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
//src/a.txt
//对于输出流 文件不存在? 是否会报错? 不会报错 文件会自动创建(File.createNewFile())
//一定要保证父级目录要存在 src/demo/a.txt 连父级目录demo都没有,则出错。不可能连父级目录也创建出来
//读写内容: 出现乱码 编码格式不符
//字节流读取字符数据,仅对于汉字,可能会出现乱码。
OutputStream outputStream = new FileOutputStream("src/a.txt", true); //不加true,默认覆盖。加true,在文件末尾添加
try (outputStream) {
outputStream.write(97); //a
outputStream.write("张三".getBytes());
outputStream.write("97".getBytes());
outputStream.write('\n');
outputStream.write("我们".getBytes(Charset.forName("UTF-8"))); //在指定格式下编码
//outputStream.write("世界那么大".getBytes(), 0, 5);
} catch (IOException e) {
e.printStackTrace();
}
}
}
(3)字节流案例--文件上传
/**
* @author: sunshine
* @description: 模拟用户上传头像
* @data: 2022/3/7 21:26
* @version: 0.1
* @since: jdk11
*/
//流程:1.告知文件在哪?--上传文件路径 2.读取用户传的数据,写入服务器中的一个文件里(要保证文件名称唯一)
public class UploadImageDemo {
private static final String PARENT_DIRECTORY = "upload/user/"; //假设要将文件传到day19里的upload文件夹里的user文件夹里
public static String upload(String sourceFilePath) throws FileNotFoundException { //返回值是上传成功后的文件路径 形参是告知的要上传的文件路径
Objects.requireNonNull(sourceFilePath);
//文件很大,循环读写文件数据
//1.读取源文件数据 InputStream
//2.写入目标文件中 OutputStream
InputStream inputStream = new FileInputStream(sourceFilePath);
//获得目标文件名称
String fileName = sourceFilePath.substring(sourceFilePath.lastIndexOf(File.separator) + 1); //获得源文件名称,最后\之后的数据
OutputStream outputStream = new FileOutputStream(PARENT_DIRECTORY+fileName); //目标文件路径
try (inputStream;outputStream){
//循环读写数据
int len; //有效字节数
while ((len=inputStream.read())!=-1){
outputStream.write(len);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件上传成功");
return PARENT_DIRECTORY+fileName; //PARENT_DIRECTORY+文件名+后缀
}
public static void main(String[] args) throws FileNotFoundException {
String path = "E:\\io\\tom.jpg"; //要上传的文件路径
System.out.println(upload(path)); //传过去要上传路径,返回上传后的路径。(回显图片)
}
}
不同用户上传重名文件时:
/**
* @author: sunshine
* @description: 模拟用户上传头像
* @data: 2022/3/7 21:26
* @version: 0.1
* @since: jdk11
*/
public class UploadImageDemo {
private static final String PARENT_DIRECTORY = "upload/user/";
public static String upload(String sourceFilePath) throws FileNotFoundException {
Objects.requireNonNull(sourceFilePath);
InputStream inputStream = new FileInputStream(sourceFilePath);
//String fileName = sourceFilePath.substring(sourceFilePath.lastIndexOf(File.separator) + 1);
//保证名称唯一性,用UUID(128位的无符号的16进制的字符串,几乎不可能重复),一般不要其中的-
String extension = sourceFilePath.substring(sourceFilePath.lastIndexOf("."));
String targetFileName = UUID.randomUUID().toString().replaceAll("-","")+extension;
OutputStream outputStream = new FileOutputStream(PARENT_DIRECTORY+targetFileName);
try (inputStream;outputStream){
int len;
while ((len=inputStream.read())!=-1){
outputStream.write(len);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件上传成功");
return PARENT_DIRECTORY+targetFileName;
}
public static void main(String[] args) throws FileNotFoundException {
//String path = "E:\\io\\tom.jpg";
String path1 = "D:\\io\\tom.jpg"; //另一个用户又上传了一个同名的图片
System.out.println(upload(path1));
}
}
若上传大量文件,将文件传到多个目录中:
/**
* @author: sunshine
* @description: 模拟用户上传头像
* @data: 2022/3/7 21:26
* @version: 0.1
* @since: jdk11
*/
public class UploadImageDemo {
private static final String PARENT_DIRECTORY = "upload/user/";
public static String upload(String sourceFilePath) throws FileNotFoundException {
Objects.requireNonNull(sourceFilePath);
InputStream inputStream = new FileInputStream(sourceFilePath);
//用户量增加,解决所有文件都在同一个目录的问题,用分目录存储图片
//一般以时间命名目录
//动态创建目录,目录名以当前时间为目录名称 2022-03-07这样的格式
String curDate = LocalDate.now().toString(); //当前时间
//判断目录是否存在
File directory = new File(PARENT_DIRECTORY,curDate);
if(!directory.exists()){
directory.mkdirs(); //若不存在,创建目录
}
String extension = sourceFilePath.substring(sourceFilePath.lastIndexOf("."));
String targetFileName = UUID.randomUUID().toString().replaceAll("-","")+extension;
OutputStream outputStream = new FileOutputStream(new File(directory,targetFileName)); //这里也变动
try (inputStream;outputStream){
int len;
while ((len=inputStream.read())!=-1){
outputStream.write(len);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件上传成功");
return PARENT_DIRECTORY+curDate+"/"+targetFileName; //多了一级目录
}
public static void main(String[] args) throws FileNotFoundException {
//String path = "E:\\io\\tom.jpg";
String path1 = "D:\\io\\tom.jpg";
System.out.println(upload(path1));
}
}
若用户量更多,以后还有目录打散(利用哈希表)的方法。
若上传文件很大:
/**
* @author: sunshine
* @description: 模拟用户上传头像
* @data: 2022/3/7 21:26
* @version: 0.1
* @since: jdk11
*/
public class UploadImageDemo {
private static final String PARENT_DIRECTORY = "upload/user/";
public static String upload(String sourceFilePath) throws FileNotFoundException {
Objects.requireNonNull(sourceFilePath);
InputStream inputStream = new FileInputStream(sourceFilePath);
String curDate = LocalDate.now().toString();
File directory = new File(PARENT_DIRECTORY,curDate);
if(!directory.exists()){
directory.mkdirs();
}
String extension = sourceFilePath.substring(sourceFilePath.lastIndexOf("."));
String targetFileName = UUID.randomUUID().toString().replaceAll("-","")+extension;
OutputStream outputStream = new FileOutputStream(new File(directory,targetFileName));
try (inputStream;outputStream){
//目前是读一个字节就写一个字节,很慢
//优化,来个缓冲,就是byte[] (再大的是多任务,多线程完成的)
byte[] bytes = new byte[1024]; //一次读1024个字节放到字节数组中
int len;
while ((len=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len); //这里用带三个参的,给有效字节,若用write(byte[]),写的东西肯定大于源文件
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件上传成功");
return PARENT_DIRECTORY+curDate+"/"+targetFileName;
}
public static void main(String[] args) throws FileNotFoundException {
long begin = System.nanoTime(); //开始时间,纳秒
//String path = "E:\\io\\tom.jpg";
String path1 = "D:\\io\\tom.jpg";
System.out.println(upload(path1));
long end = System.nanoTime();
System.out.println(end-begin); //计算上传时间
}
}
大文件,不用缓冲,用高效流(底层自带缓冲,8192)替换也可以:
/**
* @author: Lisa
* @className: BufferDemo
* @description: 高效字节流
* @date: 2022/3/7 16:35
* @version: 0.1
* @since: jdk11
*/
public class BufferDemo {
public static void main(String[] args) throws FileNotFoundException {
//模拟文件上传:
long begin = System.currentTimeMillis();
String path = "E:\\tools\\IntelliJ IDEA\\ideaIU-2020.1.1.exe";
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(path));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("D:\\a.exe"));
try (inputStream; outputStream) {
//循环读写
int len;
byte[] bytes = new byte[1024 * 5]; //底层自带数组,若自定义数组
//自定义的缓冲 >底层的缓冲 就不使用底层的缓冲
//自定义的缓冲 < 底层的缓冲 8192
while ((len = inputStream.read(bytes)) != -1) {
//一次读取一个字节 一次写一次字节
outputStream.write(bytes,0,len); //将1个字节写入缓冲,当缓冲满的时候,调用底层的flush,写到文件
}
} catch (IOException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - begin); //8
}
}
(4)字符流
/**
* @author: Lisa
* @className: RederDemo
* @description:
* @date: 2022/3/7 16:58
* @version: 0.1
* @since: jdk11
*/
public class ReaderDemo {
public static void main(String[] args) {
try {
demo2();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void demo2() throws IOException {
//创建字符输出流对象
@Cleanup //close注解
FileWriter writer = new FileWriter("src/a.txt");
writer.write('a');
writer.write('\n');
writer.write('好');
writer.write('\n');
writer.write(97);
writer.write("hello world 你好 世界");
}
private static void demo1() throws FileNotFoundException {
//1.创建字符输入流对象 Reader
Reader reader = new FileReader("src/info.txt");
try (reader) {
//2.循环读取文件数据
//reader.read(); //一次读取一个字符 -1
/*int data = reader.read();
System.out.println((char) data);*/
/*int len;
while ((len=reader.read())!=-1){
System.out.print((char)len);
}*/
//reader.read(char[] chs); 一次读取length个字符数据 返回值: 有效的字符个数
/* char[] chars = new char[20];
int len = reader.read(chars);
//将chars数组转字符串
System.out.println(new String(chars, 0, len));*/
/*char[] chars = new char[20];
int len;
while ((len=reader.read(chars))!=-1){
System.out.print(new String(chars, 0, len));
}*/
} catch (IOException e) {
e.printStackTrace();
}
}
}
(5)字符流案例
模拟用户注册,持久化保存
/**
* @author: sunshine
* @description:
* @data: 2022/3/7 22:52
* @version: 0.1
* @since: jdk11
*/
public class UserDemo {
public static void main(String[] args) throws IOException {
userRegister();
}
private static int idIndex = 1001;
private static void userRegister() throws IOException {
//模拟用户注册:持久化保存用户信息
//将每个用户信息保存到userInfo.txt id-name-pass-age 一行存一个信息
@Cleanup
FileWriter writer = new FileWriter("src/userInfo.txt",true); //将用户信息写入文件中
@Cleanup
Scanner input = new Scanner(System.in);
String answer;
do {
System.out.println("请录入用户name");
String name = input.nextLine();
System.out.println("请录入用户pass");
String pass = input.nextLine();
System.out.println("请录入用户age");
String age = input.nextLine();
writer.write(String.join("-",String.valueOf(idIndex++),name,pass,age));
writer.write("\n"); //换行
writer.flush(); //写一次看一次
System.out.println("是否继续?y/n");
answer = input.nextLine();
} while (Objects.equals("y",answer));
System.out.println("注册结束");
}
}
多个用户注册结束,再次注册,id会从1001开始,如何解决?
/**
* @author: sunshine
* @description:
* @data: 2022/3/7 22:52
* @version: 0.1
* @since: jdk11
*/
@Data
class UserInfo{
private Integer id;
private String name;
private String pass;
private Integer age;
}
public class UserDemo {
public static void main(String[] args) throws IOException {
userRegister();
}
public static List<UserInfo> findAllUser() throws IOException {
//查询所有用户,获得最后一个用户的id
//将每一行记录装配成一个个用户对象(读取文件里的每一行记录,分割,再装配)
List<UserInfo> userInfoList = new ArrayList<>(10);
@Cleanup
BufferedReader reader = new BufferedReader(new FileReader("src/userInfo.txt"));
//String line = reader.readLine(); //读到末尾为null
String line;
while ((line=reader.readLine())!=null){
String[] infoArray = line.split("-");
UserInfo userInfo = new UserInfo();
userInfo.setId(Integer.parseInt(infoArray[0]));
userInfo.setName(infoArray[1]);
userInfo.setPass(infoArray[2]);
userInfo.setAge(Integer.parseInt(infoArray[3]));
userInfoList.add(userInfo);
}
return userInfoList;
}
private static int idIndex = 1001;
private static void userRegister() throws IOException {
//模拟用户注册:持久化保存用户信息
//将每个用户信息保存到userInfo.txt id-name-pass-age 一行存一个信息
@Cleanup
FileWriter writer = new FileWriter("src/userInfo.txt",true); //将用户信息写入文件中
List<UserInfo> userInfoList = findAllUser();
if(!userInfoList.isEmpty()){
idIndex=userInfoList.get(userInfoList.size()-1).getId();
}
@Cleanup
Scanner input = new Scanner(System.in);
String answer;
do {
System.out.println("请录入用户name");
String name = input.nextLine();
System.out.println("请录入用户pass");
String pass = input.nextLine();
System.out.println("请录入用户age");
String age = input.nextLine();
//新注册用户信息id,要看之前注册过没有
writer.write(String.join("-",String.valueOf(++idIndex),name,pass,age));
writer.write("\n"); //换行
writer.flush(); //写一次看一次
System.out.println("是否继续?y/n");
answer = input.nextLine();
} while (Objects.equals("y",answer));
System.out.println("注册结束");
}
}









