关于Java从本地文件复制到网络文件上传
作者:CrazyDragon_King 发布时间:2022-05-10 16:49:02
文件复制和文件上传
最近在看文件和IO流相关的东西,写了一些代码,发现这个有很多很有趣的地方。特别是对 File 和 IO 流的使用之后,我对这部分知识有了更深入的理解。今天就尝试了从本地文件复制到网络文件上传,发现这部分其实是很相似的,都是将文件从一个地方转移到另一个地方,这也是流的特点之一。 相信,看我博客之后,你也会有相同的理解。
文件复制
文件复制: 将一个本地文件从一个目录,复制到另一个目录。(通过本地文件系统)
主要代码
package dragon;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 本地文件复制:
* 将文件从一个地方复制到另一个地方。
*
* @author Alfred
* */
public class FileCopy {
public FileCopy() {}
public void fileCopy(String target, String output) throws IOException {
File targetFile = new File(target);
File outputPath = new File(output);
this.init(targetFile, outputPath);
/**注意这里使用了 try with resource 语句,所以不需要显示的关闭流了。
* 而且,再关闭流操作中,会自动调用 flush 方法,如果不放心,
* 可以在每个write 方法后面,强制刷新一下。
* */
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(targetFile)); //创建输出文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(outputPath, "copy"+targetFile.getName())))){
int hasRead = 0;
byte[] b = new byte[1024];
while ((hasRead = bis.read(b)) != -1) {
bos.write(b, 0, hasRead);
}
}
System.out.println("文件复制成功");
}
//数据校验及初始化工作
private void init(File targetFile, File outputPath) throws FileNotFoundException {
if (!targetFile.exists()) {
throw new FileNotFoundException("目标文件不存在:"+targetFile.getAbsolutePath());
} else {
if (!targetFile.isFile()) {
throw new FileNotFoundException("目标文件是一个目录:"+targetFile.getAbsolutePath());
}
}
if (!outputPath.exists()) {
if (!outputPath.mkdirs()) {
throw new FileNotFoundException("无法创建输出路径:"+outputPath.getAbsolutePath());
}
} else {
if (!outputPath.isDirectory()) {
throw new FileNotFoundException("输出路径不是一个目录:"+outputPath.getAbsolutePath());
}
}
}
}
测试类
package dragon;
import java.io.IOException;
public class FileCopyTest {
public static void main(String[] args) throws IOException {
String target = "D:/DB/BuilderPattern.png";
String output = "D:/DBC/dragon/";
FileCopy copy = new FileCopy();
copy.fileCopy(target, output);
}
}
执行结果
注意:右边文件是复制的结果,左边的不是。(下面会提到!)
说明
上面的代码只是将一个本地文件从一个目录,复制到另一个目录,还是比较简单的,这只是一个原理性的代码,来说明输入输出流的应用。将文件从一个地方复制到另一个地方。
网络文件传输(TCP)
**网络文件传输(TCP):**使用套接字(TCP)进行演示,文件从一个地方复制到另一个地方。(通过网络的方式。)
主要代码
Server
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
try (
ServerSocket server = new ServerSocket(8080)){
Socket client = server.accept();
//开始读取文件
try (
BufferedInputStream bis = new BufferedInputStream(client.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("D:/DBC/dragon", System.currentTimeMillis()+".jpg")))){
int hasRead = 0;
byte[] b = new byte[1024];
while ((hasRead = bis.read(b)) != -1) {
bos.write(b, 0, hasRead);
}
}
System.out.println("文件上传成功。");
}
}
}
Client
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
try (Socket client = new Socket("127.0.0.1", 8080)){
File file = new File("D:/DB/netFile/001.jpg");
//开始写入文件
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(client.getOutputStream())){
int hasRead = 0;
byte[] b = new byte[1024];
while ((hasRead = bis.read(b)) != -1) {
bos.write(b, 0, hasRead);
}
}
}
}
}
执行效果
执行程序
注意:这个上传文件的目录和本地文件复制是在同一个目录,但是使用的方式不一样,文件的命名方式不一样,使用的是当前的毫秒数。 复制前文件
复制后文件
说明
通过网络的方式使用流,使用传输层的TCP协议,绑定了 8080 端口,这里需要一些网络的知识,不过都是最基本的知识。可以看出来,上面这个 Server端和 Client端的代码很简单,甚至都没有实现传输文件的后缀名!(哈哈,其实是我对套接字编程不太熟悉,传输文件名的话,我一开始尝试,但是没有成功。不过这个不影响这个例子,套接字我会抽时间来看的。哈!) 注意这里我要表达的意思通过网络将文件从一个地方复制到另一个地方。(使用较为的是传输层的协议)
网络文件传输(HTTP)
HTTP 是建立在 TCP/IP 协议之上的应用层协议,传输层协议使用起来感觉还是比较麻烦的,不如应用层协议用起来方便。
网络文件传输(HTTP): 这里使用 Servlet(3.0以上)(JSP)技术来举例,就以我们最常使用的文件上传为例。
使用 HTTP 协议将文件从一个地方复制到另一个地方。
使用 apache 组件实现文件上传
注意:因为原始的通过 Servlet 上传文件较为麻烦,现在都是使用一些组件来达成这个文件上传的功能的。(我没有找到文件上传最原始的写法,想必应该是很繁琐的吧!) 这里使用两个jar包:
commons-fileupload-1.4.jar
commons-io-2.6.jar
注意:在 apache 网站可以下载到。
上传文件的 Servlet
package com.study;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* Servlet implementation class UploadServlet
*/
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//如果不是文件上传的话,直接不处理,这样比较省事
if (ServletFileUpload.isMultipartContent(request)) {
//获取(或者创建)上传文件的路径
String path = request.getServletContext().getRealPath("/image");
File uploadPath = new File(path);
if (!uploadPath.exists()) {
uploadPath.mkdir();
}
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items;
try {
items = upload.parseRequest(request);
Iterator<FileItem> it = items.iterator();
while (it.hasNext()) {
FileItem item = it.next();
//处理上传文件
if (!item.isFormField()) {
String filename = new File(item.getName()).getName();
System.out.println(filename);
File file = new File(uploadPath, filename);
item.write(file);
response.sendRedirect("success.jsp");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
上传文件的jsp中,只需要一个form表单即可。
<h1>文件上传</h1>
<form action="NewUpload" method="post" enctype="multipart/form-data">
<input type="file" name="image">
<input type="submit" value="上传">
</form>
运行效果
说明
虽然这样处理对于上传文件很好,但是因为使用的都是较为成熟的技术,对于想了解输入输出流的我们来说,就不是那么好了。从这个例子中,基本上看不到输入输出流的用法了,都被封装起来了。
使用 Servlet 3.0 以后的新技术实现文件上传
package com.study;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
/**
* Servlet implementation class FileUpload
*/
@MultipartConfig
@WebServlet("/FileUpload")
public class FileUpload extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part = request.getPart("image");
String header = part.getHeader("Content-Disposition");
System.out.println(header);
String filename = header.substring(header.lastIndexOf("filename=\"")+10, header.lastIndexOf("\""));
String fileSuffix = filename.lastIndexOf(".") != -1 ? filename.substring(filename.lastIndexOf(".")) : "";
String uploadPath = request.getServletContext().getRealPath("/image");
File path = new File(uploadPath);
if (!path.exists()) {
path.mkdir();
}
filename = UUID.randomUUID()+fileSuffix;
try (
BufferedInputStream bis = new BufferedInputStream(part.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(path, filename)))){
int hasRead = 0;
byte[] b = new byte[1024];
while ((hasRead = bis.read(b)) != -1) {
bos.write(b, 0, hasRead);
}
}
response.sendRedirect("success.jsp");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
使用 Servlet 3.0 的新特性实现,这里使用了 @MultipartConfig
注解。(如果不使用这个注解,会无法正常工作!感兴趣的,可以多去了解一下。)
注意:下面这段代码,这里我舍近求远了,但是这正是我想要看到的。同样是输入输出流,注意这个和上面的几个例子进行对比。
try (
BufferedInputStream bis = new BufferedInputStream(part.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(path, filename)))){
int hasRead = 0;
byte[] b = new byte[1024];
while ((hasRead = bis.read(b)) != -1) {
bos.write(b, 0, hasRead);
}
}
不使用 apache 组件的更为简单的方式是下面这种:
package com.study;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
/**
* Servlet implementation class NewUpload
*/
@MultipartConfig
@WebServlet("/NewUpload")
public class NewUpload extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part = request.getPart("image");
String header = part.getHeader("Content-Disposition");
System.out.println(header);
String filename = header.substring(header.lastIndexOf("filename=\"")+10, header.lastIndexOf("\""));
String fileSuffix = filename.lastIndexOf(".") != -1 ? filename.substring(filename.lastIndexOf(".")) : "";
String uploadPath = request.getServletContext().getRealPath("/image");
File path = new File(uploadPath);
if (!path.exists()) {
path.mkdir();
}
filename = uploadPath+File.separator+System.currentTimeMillis()+UUID.randomUUID().toString()+fileSuffix;
part.write(filename);
response.sendRedirect("success.jsp");
}
}
真正写入文件的只有这一步了,前面全是处理文件名和上传文件路径相关的代码。使用 HTTP 的这三种方式都有处理文件名和上传文件路径这段代码。
如果通过这段代码,那就更看不出什么东西来了,只知道这样做,一个文件就会被写入相应的路径。
part.write(filename);
总结
这里从本地文件复制 -> 传输层文件复制 -> 应用层文件复制,一起回顾了流的使用以及它们之间的共通点。这里说复制似乎不是很好,但是一时也想不出来什么好的说法,就凑合着看吧。这里的复制指的是:通过流将一个文件从一个地方传到另一个地方。无论是本地文件系统还是通过 TCP协议或者 HTTP协议实现。 这几种方式虽然用法都是不同的,但是仔细观察还是能看出来相通之处,无论使用那种方式都离不开输入、输出流的使用。使用 HTTP 的方式,大多使用较为成熟的技术,一般不会使用这么原始的代码来进行文件上传(复制),但是第二种方式(舍近求远)中也是使用了流进行文件上传演示,所以也是可以看出来的,虽然使用HTTP的三种方式不一样,但是底层实现,也是无法脱离 IO流的。 这里也可以看出来,Java EE 技术也是需要很好的 Java SE作为基础的,如果你能熟练掌握 Java SE的文件复制(单个文件复制、整个目录复制)、文件合并等操作,学习 Java EE的文件上传下载也是很轻松的。(但是,必要的网络基础知识还是需要掌握的,这样有助于理解这个网络的体系。)
结语: 熟练的掌握IO流的使用(我也没有做到,哈哈!),可以帮助我们做很多事情,比如使用 Python 编写爬虫很简单,但是如果掌握爬虫的基本原理,我们也可以通过Java来实现。(这就对你对JavaIO流要有一定的掌握,否则会感觉到很难做到。)今天这个博客,主要是想说明,上面几种方式之间的共通性,不知到你有没有理解到。(具体的细节我没有怎么说明,不过这都是一些基础知识,应该是不难的。)
来源:https://blog.csdn.net/qq_40734247/article/details/103828554


猜你喜欢
- java获取map中value最大值public static void main(String[] args) throws Interr
- 上一节初步了解了Android端的贝塞尔曲线,这一节就举个栗子练习一下,仿QQ未读消息气泡,是最经典的练习贝塞尔曲线的东东,效果如下附上gi
- 方法一、利用控件或窗体的Paint事件中的PainEventArgs在窗体或控件的Paint事件中接收对图形对象的引用,作为PaintEve
- mapper-locations的作用说明1、mapper-locationsmapper-locations是一个定义mapper接口位置
- 这篇文章主要介绍了spring boot 2整合swagger-ui过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的
- 前言在Android开发过程中,Bitmap往往会给开发者带来一些困扰,因为对Bitmap操作不慎,就容易造成OOM(Java.lang.O
- OleDbConnection,OracleConnection 或者SqlConnection这种连接,直接执行sql语句。现在的连接方式
- 引言RecyclerView是在Android5.0版本作为support-v7的一个组件出现,高效替代了最初的ListView等列表组件。
- 一、Kotlin 调用 Java1. kotlin 关键字转义java 中的方法或变量 是 kotlin 的关键字时,使用反引号 `` 对关
- 项目中用到WebView加上进度条放在顶部,让用户知道加载进度情况,可以提高用户体验:效果:布局:<RelativeLayoutand
- 最近为公司做的一个Demo里面用到了ScrollView嵌套了GridView和ListView,然而在嵌套的时候我发现GridView和L
- 本文实例为大家分享了Android Studio实现简易计算器App的具体代码,供大家参考,具体内容如下效果演示布局文件<?xml v
- 在一些场合,我们往往需要使用印章来给每页文档加盖一个印章,以表示该文档经过某个部门的认证的,常规的做法就是打印文档后盖章,如果需要电子档再行
- 访问权限符:(1)public:对于成员来说:任何其他类都可以访问它们,不管在同一个包中还是在另外的包中。对于类来说:  
- 本文实例讲述了Android编程防止进程被第三方软件杀死的方法。分享给大家供大家参考,具体如下:项目测试的时候发现,按home键回到桌面,再
- 1. 对图片本身进行操作尽量不要使用 setImageBitmap、setImageResource、 BitmapFactory.deco
- 本文实例讲述了Android编程简单设置ListView分割线的方法。分享给大家供大家参考,具体如下:<LinearLayout xm
- 目录第一章 前言概述第01节 概述第02节 区别第二章 核心代码第01节 成员变量第02节 构造方法第三章 扩容操作第01节 扩容代码第一章
- 最近在维护项目,app遇到安装在高版本的Android时,以往直接授权和new File(path)的形式不再支持,日志也是说Permiss
- JVM 的主要作用是什么?JVM 就是 Java Virtual Machine(Java虚拟机)的缩写,JVM 屏蔽了与具体操作系统平台相