(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("注册结束"); } }