文件上传相关
IO流
io流介绍:
IO,即`in`和`out`,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。
Java 中是通过流处理IO 的,那么什么是流?
流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。
当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
一般来说关于流的特性有下面几点:
- 先进先出:最先写入输出流的数据最先被输入流读取到。
- 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(
RandomAccessFile
除外) - 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
stream:
什么是流(Stream):
流就是一系列的数据。流是个抽象的概念,是对输入输出设备的抽象,当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
输入流(InputStream)与输出流(OutputStream):
用于以字节的形式读取和写入数据
@Test
public static void main(String[] args) throws FileNotFoundException {
// 创建基于文件的输入流
File file = new File("D:\\CloudMusic\\测试小说输入.txt");
// 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中
FileInputStream fileInputStream=new FileInputStream(file);
//创建基于文件的输出流
File file1 = new File("D:\\CloudMusic\\测试小说输出.txt");
//通过这个输出流,就可以将内存中的数据,输出到硬盘的文件中
FileOutputStream fileOutputStream = new FileOutputStream(file1);
}
字节流:
-
字节流读取:InputStream是字节输入流,同时也是抽象类,只提供方法的声明,不提供方法的实现,FileInputStream是InputStream的子类,以FileInputSream为例进行文件读取
File f = new File("D:/CloudMusic/测试.txt"); //创建基于文件的输入流 FileInputStream fis=new FileInputStream(f); //创建一个字节数组,长度为文件的长度 byte [] all=new byte[(int)f.length()]; //以字节流的形式读取文件的所有内容 fis.read(all); for (byte b : all) { System.out.println(b); } //使用完流之后应进行关闭 fis.close();
-
字节流写入:OutoutStream是字节输出流,同时也是抽象类,只提供方法的声明,不提供方法的具体实现
FileOutputStream是OutputStream的子类,以FileOutputStream为例向文件写入数据//准备文件 测试1.txt 其中的内容是空的 File f = new File("D:/CloudMusic/测试1.txt"); //创建一个字节数组,长度为文件的长度 byte [] date = {88,89}; //创建基于文件的输出流 FileOutputStream fos=new FileOutputStream(f); //把数据写入到输出流 fos.write(date); //使用完流之后应进行关闭 fos.close();
字符流:
-
FileReader是reader的子类,以FileReader为例进行文件读取
File f = new File("D:/CloudMusic/测试.txt"); try { FileReader fr= new FileReader(f); char [] date = new char[(int) f.length()]; fr.read(date); for (char c : date) { System.out.print(c); } } catch (IOException e) { e.printStackTrace(); }
-
FileWriter是Writer的子类,以FileWriter为例,把字符串写入到文件
File f = new File("D:/CloudMusic/caca.txt"); try { FileWriter fw=new FileWriter(f); String date = "abcdedf1234567890"; fw.write(date); fw.close(); } catch (IOException e) { e.printStackTrace(); }
缓存流:BufferedReader、BufferedWriter
当我们在硬盘上进行读写操作时,都会访问硬盘,如果读写的频率较高的时候,就会耗费大量的性能。为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读取较多的数据到缓存中去,以后的每一次读取,都在缓存中访问,知道缓存中的数据读取完毕,再到硬盘中去读取
-
使用缓存流输出数据到文件
File f = new File("D:/CloudMusic/测试.txt"); // 向文件测试.txt中写入三行语句 FileWriter fw = new FileWriter(f); BufferedWriter bw= new BufferedWriter(fw); bw.write("全球最大的中文搜索引擎"); bw.newLine();//添加一个新的行,相当于换行符 bw.write("致力于让网民更便捷地获取信息,找到所求。"); bw.newLine(); bw.write("百度超过千亿的中文网页数据库,可以瞬间找到相关的搜索结果。"); /*关闭流,如果不关闭的话,会存在缓存中,待虚拟机关闭后 *就会消失(数据不会写入到硬盘中) */ bw.close();
-
使用缓存流读取数据
//随便准备一个有内容的文件 File f = new File("D:/CloudMusic/测试小说.txt"); //创建文件字符流 FileReader fr=new FileReader(f); //缓存流必须建立在一个已经存在的流的基础上 BufferedReader br=new BufferedReader(fr); while(true){ //一次读取一行 String line = br.readLine(); if(null==line){ break; } System.out.println(line); }
-
flush:有的时候,需要立即把数据写入硬盘中,而不是等到缓存满了才写进去,这时候就需要用到flush
flush方法和close方法的区别:close释放资源前进行一次刷新流,close执行后不能在写内容;而flush是刷新流,执行完毕依旧可以写内容;
File f = new File("D:/CloudMusic/测试.txt"); // 向文件 测试.txt中写入三行语句 FileWriter fw = new FileWriter(f); BufferedWriter bw= new BufferedWriter(fw); //不添加close关闭流,就没有将信息写入到文件 bw.write("全球最大的中文搜索引擎"); bw.flush(); bw.newLine();//添加一个新的行,相当于换行符 bw.write("致力于让网民更便捷地获取信息,找到所求。"); bw.newLine(); bw.write("百度超过千亿的中文网页数据库,可以瞬间找到相关的搜索结果。");
对象流:
对象流指的是可以直接把一个对象以流的形式传输给其他介质,比如硬盘等
一个对象以流的形式进行传输,叫做序列化。该对象所对应的类,必须实现Serializable接口
注意:一个对象序列化有一个前提:这个对象类,必须实现了Serializable接口
-
序列化: 是对象转换成一个字节序列的过程,是一个写操作
class.Msg implements Serializable{ } ObjectOutStream oos = new ObjectOutputStream( //(文件输入流/网络输出流) oos.writeObject(); oos.flush(); oos.close()
-
反序列化: 一个字节序列转换成对象的过程 ,是一个读操作
ObjectInputStream ois = new ObjectInputStream( //(文件输入流/网络输入流) Msg msg = (Msg)ois.readObject(); ois.close()
字节流与字符流的区别:
-
字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符
-
字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件
-
任何终端(网络、文件)读取或者输出的数据都一定是字节,但是字符是经过内存处理后的数据
字符输入:字节(磁盘)–> 自动转换为 –>字符(内存); 字符输出:字符(内存)–> 自动转换为–>字节(磁盘);
在利用字符流输出的时候,所有的内容实际上都只是输出到了缓冲区中(内存)。在使用close()方法关闭的时候会将我们缓冲区的数据进行输出,如果没有关闭,那么就将无法进行输出,此时可以利用flush()方法进行强制的输出
-
Java中字符是采用Unicode标准,Unicode 编码中,一个英文字母或一个中文汉字为两个字节。而在UTF-8编码中,一个中文字符是3个字节。如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流
中文乱码情况:
-
响应-字节流:输出中文问题:
/** * 演示字节流输出的乱码问题 */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 问题: * String str = "字节流中文乱码问题"; * 使用字节流输出,会不会产生中文乱码? * 答案: * 会产生乱码 * 原因: * String str = "字节流中文乱码问题"; 在保存时用的是IDEA创建文件使用的字符集UTF-8。 * 到浏览器上显示,chrome浏览器和ie浏览器默认的字符集是GB2312(其实就是GBK),存和取用的不是同一个码表,就会产生乱码。 * * 引申: * 如果产生了乱码,就是存和取用的不是同一个码表 * 解决办法: * 把存和取的码表统一。 */ String str = "字节流输出中文的乱码问题";//UTF-8的字符集,此时浏览器显示也需要使用UTF-8的字符集。 //1.拿到字节流输出对象 ServletOutputStream sos = response.getOutputStream(); /** * 解决办法: * 第一种解决办法: * 修改浏览器的编码,使用右键——编码——改成UTF-8。(不推荐使用,我们的应用尽量不要求用户取做什么事情) * ie和火狐浏览器可以直接右键设置字符集。而chrome需要安装插件,很麻烦。 * 第二种解决办法: (不建议使用,因为不好记) * 向页面上输出一个meta标签,内容如下: <meta http-equiv="content-type" content="text/html;charset=UTF-8"> * 其实它就是指挥了浏览器,使用哪个编码进行显示。 * 第三种解决办法: * 设置响应消息头,告知浏览器响应正文的MIME类型和字符集 * response.setHeader("Content-Type","text/html;charset=UTF-8"); * 第四种解决办法:我们推荐使用的办法 * 它的本质就是设置了一个响应消息头 * response.setContentType("text/html;charset=UTF-8"); */ //第二种解决办法:sos.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>".getBytes()); //第三种解决办法:response.setHeader("Content-Type","text/html;charset=UTF-8"); //第四种解决办法: response.setContentType("text/html;charset=UTF-8"); //2.把str转换成字节数组之后输出到浏览器 sos.write(str.getBytes("UTF-8")); }
-
响应-字符流输出中文问题:
/** * 字符流输出中文乱码 * @param request * @param response * @throws ServletException * @throws IOException */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String str = "字符流输出中文乱码"; //response.setCharacterEncoding("UTF-8"); //设置响应正文的MIME类型和字符集 response.setContentType("text/html;charset=UTF-8"); //1.获取字符输出流 PrintWriter out = response.getWriter(); //2.使用字符流输出中文 /** * 问题: * out.write(str); 直接输出,会不会产生乱码 * 答案: * 会产生乱码 * 原因: * 存用的什么码表:UTF-8 * 在浏览器取之前,字符流PrintWriter已经获取过一次了,PrintWriter它在取的时候出现了乱码。 * 浏览器取默认用的是GBK。(本地系统字符集) * * UTF-8(存)————>PrintWriter ISO-8859-1(取) 乱 * PrintWirter ISO-8859-1(存)————>浏览器 GBK(取) 乱 * * 解决办法: * 改变PrintWriter的字符集,PrintWriter是从response对象中获取的,其实设置response的字符集。 * 注意:设置response的字符集,需要在拿流之前。 * response.setCharacterEncoding("UTF-8"); * * response.setContentType("text/html;charset=UTF-8"); * 此方法,其实是做了两件事: * 1.设置响应对象的字符集(包括响应对象取出的字符输出流) * 2.告知浏览器响应正文的MIME类型和字符集 */ out.write(str); }
位、字节、字符:
字节(Byte)是计量单位,表示数据量多少,是计算机信息技术用于计量存储容量的一种计量单位,通常情况下一字节等于八位。
字符(Character)计算机中使用的字母、数字、字和符号,比如’A’、‘B’、’$’、’&'等。
一般在英文状态下一个字母或字符占用一个字节,一个汉字用两个字节表示。
字节与字符:
- ASCII 码中,一个英文字母(不分大小写)为一个字节,一个中文汉字为两个字节
- UTF-8 编码中,一个英文字为一个字节,一个中文为三个字节
- Unicode 编码中,一个英文为一个字节,一个中文为两个字节。
- 符号:英文标点为一个字节,中文标点为两个字节。例如:英文句号 . 占1个字节的大小,中文句号 。占2个字节的大小。
- UTF-16 编码中,一个英文字母字符或一个汉字字符存储都需要 2 个字节(Unicode 扩展区的一些汉字存储需要 4 个字节)
- UTF-32 编码中,世界上任何字符的存储都需要 4 个字节。
FTP与SFTP与HTTP
FTP:
FTP 即 文件传输协议(英语:File Transfer Protocol 的缩写)是一个用于计算机网络上在客户端和服务器之间进行文件传输的应用层协议。完整的 FTP 是由 FTP 服务器 和 FTP 客户端组成的,客户端可以将本地的文件通过 FTP 协议上传到服务器,也可以将服务器的文件下载到本地
使用 FTP 传输文件时,用户需要通过向 FTP 服务器提供凭据来获得文件传输许可。当然某些公共 FTP 服务器可能不需要凭据即可访问其文件,但是无法保证数据传输的安全性,任何未加密公共网络上的数据发送都是非常危险的,所以为了保护传输数据的安全,由 FTP 衍生而出的就是下面的两种协议:FTPS 与 SFTP
FTPS:
FPTS
有 FTPS 隐式 SSL
和 FTPS 显示 SSL
两种模式,两者都是用 SSL 加密
- FTPS 隐式 SSL:该模式通常在端口 990 上运行。在这个模式下全部数据的交换都需要在客户端和服务器之间建立 SSL 会话,并且服务器会拒绝任何不使用 SSL 进行的连接尝试。
- FTPS 显式 SSL:显示 SSL 下服务器可以同时支持 FTP 和 FTPS 会话。开始会话前客户端需要先建立与 FTP 服务器的未加密连接,并在发送用户凭证前先发送 AUTH TLS 或 AUTH SSL 命令来请求服务器将命令通道切换到 SSL 加密通道,成功建立通道后再将用户凭证发送到 FTP 服务器,从而保证在会话期间的任何命令都可以通过 SSL 通道自动加密
SFTP:
SFTP 是 Secure File Transfer Protocol 的缩写,也叫作安全文件传送协议。
如果说 FTPS 是在 FTP 协议上增加了一层 SSL ,那么 SFTP 就是是基于网络协议SSH(安全外壳)的协议与前面所说的 FTP 完全不同。SFTP 不使用单独的命令通道和数据通道,而是数据和命令都会通过单个连接以特殊格式的数据包进行传输
SFTP 提供了两种验证连接的方法:
- 与 FTP 一样,连接时只需要验证用户 ID 和密码就可以了。但是,与FTP不同的是,这些凭据是加密的,这是 SFTP 最主要的安全优势
- 除密码外,还可以通过 SSH 密钥来验证并通过 SFTP 协议连接
FTP、SFTP、FTPS 区别:
维度 | FTPS | FTP | SFTP |
---|---|---|---|
安全 | 通过隐式SSL或显式SSL在命令和数据通道上进行加密。通信是人类可读的。 | 命令和数据通道中的未加密信息交换。通信是人类可读的。 | FTP服务器和客户端之间的所有信息交换均通过SSH协议加密。 SFTP还可以加密会话。由于通讯采用二进制格式, 因此难以理解。 |
服务器的防火墙端口 | 允许端口21和/或990、989上的入站连接 | 允许端口21上的入站连接 | 允许端口22上的入站连接 |
客户端的防火墙端口 | 允许到服务器定义的端口21和被动端口范围的出站连接 | 允许到服务器定义的端口21和被动端口范围的出站连接 | 允许到端口22的出站连接 |
实战代码:
https://blog.csdn.net/u011047968/article/details/125891305?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166530710216800184133878%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=166530710216800184133878&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~pc_rank_34-9-125891305-null-null.142v52pc_rank_34_1,201v3control_2&utm_term=FTP%E4%B8%8ESFTP%E4%B8%8EHHTP&spm=1018.2226.3001.4187
HTTP:
HTTP协议是超文本传输协议,为计算机网络的应用层协议。它是基于TCP/IP协议的,客户端和服务器端的通信规则为握手规则
http协议的请求:
1、请求的组成部分
-
请求行(包括请求方式、提交的参数、HTTP版本号)
-
请求头(由多个键值对组成,对HTTP请求的各种属性进行设置说明)
名称 说明 Accept 客户端所支持的MIME类型 Accept-Encoding 客户端浏览器所支持的压缩编码格式。最常用的就是gzip压缩 Accept-Language 客户端所支持的语言,一般都是zh_CN或en_US等 Referer 告知服务器,当前请求的来源 Content-Type 请求正文所支持的MIME类型 Content-Lenngth 请求正文的长度 User-Agent 浏览器的相关信息 Connection 连接的状态。keep-Alive保持连接 If-Modified-Since 客户端浏览器缓存文件的最后修改时间 Cookie 会话管理相关,非常的重要 -
请求空行(可以理解为简单的换行,用于隔离请求头和请求体)
-
请求体(只有Post方式的HTTP协议才有,用于显示请求的参数)
2、请求的方式:
- get方式:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效。
- post方式:请求能携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效
http协议的响应:
1、响应的组成部分
-
响应行(请求方式、HTTP版本、状态码和状态描述)
-
响应头(由多个键值对组成,对HTTP响应的各种属性进行设置说明)
名称 说明 Location 请求重定向的地址,常与302,307配合使用 Server 服务器相关信息 Content-Type 响应正文的MIME类型 Content-Length 响应正文的长度 Content-Disposition 告知客户端浏览器,以下载的方式打开响应正文 Refresh 定时刷新 Last-Modified 服务器资源的最后修改时间 Set-Cookie 会话管理相关,非常的重要 Expires:-1 服务器资源到客户端浏览器后的缓存时间 Catch-Control:no-catch 不要缓存 -
响应空行(可以理解为简单的换行,用于隔离响应头和响应体)
-
响应体(将资源文件发送给客户端浏览器进行解析)
2、常见的状态码:
状态码 | 说明 |
---|---|
200 | 一切正常 |
302/307 | 请求重定向。两次请求,地址栏发生变化 |
304 | 请求资源未发生变化,使用缓存 |
404 | 请求资源未找到 |
500 | 服务器错误 |
FILE
File
类是用来操作文件的类,但它不能操作文件中的数据
File类的构造方法
方法名 | 说明 |
---|---|
File(File parent, String child) | 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 |
File(String pathname) | 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 |
File(URI uri) | 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 |
File类的常用方法
方法 | 说明 |
---|---|
createNewFile() | 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 |
delete() | 删除此抽象路径名表示的文件或目录。 |
exists() | 测试此抽象路径名表示的文件或目录是否存在。 |
getAbsoluteFile() | 返回此抽象路径名的绝对路径名形式。 |
getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串。 |
length() | 返回由此抽象路径名表示的文件的长度。 |
mkdir() | 创建此抽象路径名指定的目录。 |
getPath() | 将此抽象路径名转换为一个路径名字符串。 |
isDirectory() | 测试此抽象路径名表示的文件是否是一个目录。 |
isFile() | 测试此抽象路径名表示的文件是否是一个标准文件。 |
listFiles() | 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。 |
mkdirs() | 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。 |
renameTo(File dest) | 重新命名此抽象路径名表示的文件。 |
toString() | 返回此抽象路径名的路径名字符串。 |
//创建File对象
File file = new File("example.txt");
System.out.println("文件的名称:"+file.getName());
System.out.println("文件的相对路径:"+file.getPath());
System.out.println("文件的绝对路径:"+file.getAbsolutePath());
System.out.println("文件的父路径:"+file.getParent());
System.out.println(file.canRead() ? "文件可读" : "文件不可读");
System.out.println(file.canWrite() ? "文件可写" : "文件不可写");
System.out.println(file.isFile() ? "是一个文件" : "不是一个文件");
System.out.println(file.isDirectory() ? "是一个目录" : "不是一个目录");
System.out.println(file.isAbsolute() ? "是绝对路径" : "不是绝对路径");
System.out.println("最后修改时间为:"+file.lastModified());
System.out.println("文件大小为:"+file.length()+"byte");
-
遍历目录下的文件:
File类中有一个list()方法,用于遍历某个指定目录下的所有文件的名称,代码如下
//创建File对象 File file = new File("E:\\Idea\\JavaSE\\重新学习"); //判断是否时目录 if (file.isDirectory()){ //获取目录下的所有文件的名称 String[] fileNames = file.list(); //对指定路径下的文件或目录进行遍历 Arrays.stream(fileNames).forEach(f -> System.out.println(f)); }
-
遍历目录下的文件并过滤:
File类提供了一个重载的list(FilenameFilter filter)方法,该方法接收一个FilenameFilter接口类型的参数。FilenameFilter是一个函数式接口,被称作文件过滤器,接口中定义了一个抽象方法accept(File dir,String name)用于依次对指定的File的所有子目录或文件进行迭代。在调用list(FilenameFilter filter)方法时,需要实现过滤器FilenameFilter,并在accept(File dir,String name)方法中进行筛选,从而获得指定类型的文件
//创建File对象 File file = new File("E:\\Idea\\JavaSE\\重新学习\\src"); //File file = new File("E:\\Idea\\JavaSE\\重新学习\\src\\com\\Demo01"); //判断是否时目录 if (file.isDirectory()){ //获取目录下的所有文件的名称 String[] fileNames = file.list(((dir, name) -> name.endsWith(".txt"))); //String[] fileNames = file.list(((dir, name) ->name.endsWith(".java"))); //对指定路径下的文件或目录进行遍历 Arrays.stream(fileNames).forEach(f -> System.out.println(f)); }
-
输出一个目录中的所有文件名(目录可能是多级目录,如a目录中有b、c目录。。。)
public static void listDir( String dir) throws IOException{ File file = new File(dir); // 传进来的可能不是一个路径 if(!file.isDirectory()){ throw new IOException(dir + "不是目录"); } // 传进来的可能是一个错误的路径 if(file == null){ throw new IOException("没有此路径"); } File[] files = file.listFiles(); for(File f:files){ // 有可能是一个多级目录,递归调用 if(f.isDirectory()){ listDir(f.getAbsolutePath()); }else{ // 是文件就直接输出该文件的绝对路径 System.out.println(f.getAbsolutePath()); } } } //执行结果: //E:\aa\a.txt //E:\aa\b.txt //E:\aa\cc\a.txt //E:\aa\cc\b.txt