關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳
文件復(fù)制和文件上傳
最近在看文件和IO流相關(guān)的東西,寫(xiě)了一些代碼,發(fā)現(xiàn)這個(gè)有很多很有趣的地方。特別是對(duì) File 和 IO 流的使用之后,我對(duì)這部分知識(shí)有了更深入的理解。今天就嘗試了從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳,發(fā)現(xiàn)這部分其實(shí)是很相似的,都是將文件從一個(gè)地方轉(zhuǎn)移到另一個(gè)地方,這也是流的特點(diǎn)之一。 相信,看我博客之后,你也會(huì)有相同的理解。
文件復(fù)制
文件復(fù)制: 將一個(gè)本地文件從一個(gè)目錄,復(fù)制到另一個(gè)目錄。(通過(guò)本地文件系統(tǒng))
主要代碼
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; /** * 本地文件復(fù)制: * 將文件從一個(gè)地方復(fù)制到另一個(gè)地方。 * * @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 語(yǔ)句,所以不需要顯示的關(guān)閉流了。 * 而且,再關(guān)閉流操作中,會(huì)自動(dòng)調(diào)用 flush 方法,如果不放心, * 可以在每個(gè)write 方法后面,強(qiáng)制刷新一下。 * */ try ( BufferedInputStream bis = new BufferedInputStream(new FileInputStream(targetFile)); //創(chuàng)建輸出文件 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("文件復(fù)制成功"); } //數(shù)據(jù)校驗(yàn)及初始化工作 private void init(File targetFile, File outputPath) throws FileNotFoundException { if (!targetFile.exists()) { throw new FileNotFoundException("目標(biāo)文件不存在:"+targetFile.getAbsolutePath()); } else { if (!targetFile.isFile()) { throw new FileNotFoundException("目標(biāo)文件是一個(gè)目錄:"+targetFile.getAbsolutePath()); } } if (!outputPath.exists()) { if (!outputPath.mkdirs()) { throw new FileNotFoundException("無(wú)法創(chuàng)建輸出路徑:"+outputPath.getAbsolutePath()); } } else { if (!outputPath.isDirectory()) { throw new FileNotFoundException("輸出路徑不是一個(gè)目錄:"+outputPath.getAbsolutePath()); } } } }
測(cè)試類(lèi)
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); } }
執(zhí)行結(jié)果
注意:右邊文件是復(fù)制的結(jié)果,左邊的不是。(下面會(huì)提到?。?/strong>
說(shuō)明
上面的代碼只是將一個(gè)本地文件從一個(gè)目錄,復(fù)制到另一個(gè)目錄,還是比較簡(jiǎn)單的,這只是一個(gè)原理性的代碼,來(lái)說(shuō)明輸入輸出流的應(yīng)用。將文件從一個(gè)地方復(fù)制到另一個(gè)地方。
網(wǎng)絡(luò)文件傳輸(TCP)
**網(wǎng)絡(luò)文件傳輸(TCP):**使用套接字(TCP)進(jìn)行演示,文件從一個(gè)地方復(fù)制到另一個(gè)地方。(通過(guò)網(wǎng)絡(luò)的方式。)
主要代碼
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(); //開(kāi)始讀取文件 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"); //開(kāi)始寫(xiě)入文件 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); } } } } }
執(zhí)行效果
執(zhí)行程序
注意:這個(gè)上傳文件的目錄和本地文件復(fù)制是在同一個(gè)目錄,但是使用的方式不一樣,文件的命名方式不一樣,使用的是當(dāng)前的毫秒數(shù)。 復(fù)制前文件
復(fù)制后文件
說(shuō)明
通過(guò)網(wǎng)絡(luò)的方式使用流,使用傳輸層的TCP協(xié)議,綁定了 8080 端口,這里需要一些網(wǎng)絡(luò)的知識(shí),不過(guò)都是最基本的知識(shí)。可以看出來(lái),上面這個(gè) Server端和 Client端的代碼很簡(jiǎn)單,甚至都沒(méi)有實(shí)現(xiàn)傳輸文件的后綴名!(哈哈,其實(shí)是我對(duì)套接字編程不太熟悉,傳輸文件名的話,我一開(kāi)始嘗試,但是沒(méi)有成功。不過(guò)這個(gè)不影響這個(gè)例子,套接字我會(huì)抽時(shí)間來(lái)看的。哈?。?注意這里我要表達(dá)的意思通過(guò)網(wǎng)絡(luò)將文件從一個(gè)地方復(fù)制到另一個(gè)地方。(使用較為的是傳輸層的協(xié)議)
網(wǎng)絡(luò)文件傳輸(HTTP)
HTTP 是建立在 TCP/IP 協(xié)議之上的應(yīng)用層協(xié)議,傳輸層協(xié)議使用起來(lái)感覺(jué)還是比較麻煩的,不如應(yīng)用層協(xié)議用起來(lái)方便。
網(wǎng)絡(luò)文件傳輸(HTTP): 這里使用 Servlet(3.0以上)(JSP)技術(shù)來(lái)舉例,就以我們最常使用的文件上傳為例。
使用 HTTP 協(xié)議將文件從一個(gè)地方復(fù)制到另一個(gè)地方。
使用 apache 組件實(shí)現(xiàn)文件上傳
注意:因?yàn)樵嫉耐ㄟ^(guò) Servlet 上傳文件較為麻煩,現(xiàn)在都是使用一些組件來(lái)達(dá)成這個(gè)文件上傳的功能的。(我沒(méi)有找到文件上傳最原始的寫(xiě)法,想必應(yīng)該是很繁瑣的吧?。?這里使用兩個(gè)jar包:
- commons-fileupload-1.4.jar
- commons-io-2.6.jar
注意:在 apache 網(wǎng)站可以下載到。
上傳文件的 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)) { //獲?。ɑ蛘邉?chuàng)建)上傳文件的路徑 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中,只需要一個(gè)form表單即可。
<h1>文件上傳</h1> <form action="NewUpload" method="post" enctype="multipart/form-data"> <input type="file" name="image"> <input type="submit" value="上傳"> </form>
運(yùn)行效果
說(shuō)明
雖然這樣處理對(duì)于上傳文件很好,但是因?yàn)槭褂玫亩际禽^為成熟的技術(shù),對(duì)于想了解輸入輸出流的我們來(lái)說(shuō),就不是那么好了。從這個(gè)例子中,基本上看不到輸入輸出流的用法了,都被封裝起來(lái)了。
使用 Servlet 3.0 以后的新技術(shù)實(shí)現(xiàn)文件上傳
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 的新特性實(shí)現(xiàn),這里使用了 @MultipartConfig
注解。(如果不使用這個(gè)注解,會(huì)無(wú)法正常工作!感興趣的,可以多去了解一下。)
注意:下面這段代碼,這里我舍近求遠(yuǎn)了,但是這正是我想要看到的。同樣是輸入輸出流,注意這個(gè)和上面的幾個(gè)例子進(jìn)行對(duì)比。
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 組件的更為簡(jiǎn)單的方式是下面這種:
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"); } }
真正寫(xiě)入文件的只有這一步了,前面全是處理文件名和上傳文件路徑相關(guān)的代碼。使用 HTTP 的這三種方式都有處理文件名和上傳文件路徑這段代碼。
如果通過(guò)這段代碼,那就更看不出什么東西來(lái)了,只知道這樣做,一個(gè)文件就會(huì)被寫(xiě)入相應(yīng)的路徑。
part.write(filename);
總結(jié)
這里從本地文件復(fù)制 -> 傳輸層文件復(fù)制 -> 應(yīng)用層文件復(fù)制,一起回顧了流的使用以及它們之間的共通點(diǎn)。這里說(shuō)復(fù)制似乎不是很好,但是一時(shí)也想不出來(lái)什么好的說(shuō)法,就湊合著看吧。這里的復(fù)制指的是:通過(guò)流將一個(gè)文件從一個(gè)地方傳到另一個(gè)地方。無(wú)論是本地文件系統(tǒng)還是通過(guò) TCP協(xié)議或者 HTTP協(xié)議實(shí)現(xiàn)。 這幾種方式雖然用法都是不同的,但是仔細(xì)觀察還是能看出來(lái)相通之處,無(wú)論使用那種方式都離不開(kāi)輸入、輸出流的使用。使用 HTTP 的方式,大多使用較為成熟的技術(shù),一般不會(huì)使用這么原始的代碼來(lái)進(jìn)行文件上傳(復(fù)制),但是第二種方式(舍近求遠(yuǎn))中也是使用了流進(jìn)行文件上傳演示,所以也是可以看出來(lái)的,雖然使用HTTP的三種方式不一樣,但是底層實(shí)現(xiàn),也是無(wú)法脫離 IO流的。 這里也可以看出來(lái),Java EE 技術(shù)也是需要很好的 Java SE作為基礎(chǔ)的,如果你能熟練掌握 Java SE的文件復(fù)制(單個(gè)文件復(fù)制、整個(gè)目錄復(fù)制)、文件合并等操作,學(xué)習(xí) Java EE的文件上傳下載也是很輕松的。(但是,必要的網(wǎng)絡(luò)基礎(chǔ)知識(shí)還是需要掌握的,這樣有助于理解這個(gè)網(wǎng)絡(luò)的體系。)
結(jié)語(yǔ): 熟練的掌握IO流的使用(我也沒(méi)有做到,哈哈?。梢詭椭覀冏龊芏嗍虑?,比如使用 Python 編寫(xiě)爬蟲(chóng)很簡(jiǎn)單,但是如果掌握爬蟲(chóng)的基本原理,我們也可以通過(guò)Java來(lái)實(shí)現(xiàn)。(這就對(duì)你對(duì)JavaIO流要有一定的掌握,否則會(huì)感覺(jué)到很難做到。)今天這個(gè)博客,主要是想說(shuō)明,上面幾種方式之間的共通性,不知到你有沒(méi)有理解到。(具體的細(xì)節(jié)我沒(méi)有怎么說(shuō)明,不過(guò)這都是一些基礎(chǔ)知識(shí),應(yīng)該是不難的。)
到此這篇關(guān)于關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳的文章就介紹到這了,更多相關(guān)Java本地文件復(fù)制到網(wǎng)絡(luò)上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何根據(jù)帶賬號(hào)密碼的WSDL地址生成JAVA代碼
這篇文章主要介紹了如何根據(jù)帶賬號(hào)密碼的WSDL地址生成JAVA代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10詳解MyEclipse中搭建spring-boot+mybatis+freemarker框架
這篇文章主要介紹了詳解MyEclipse中搭建spring-boot+mybatis+freemarker框架,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10Java多線程(單例模式,堵塞隊(duì)列,定時(shí)器)詳解
這篇文章主要介紹了java多線程的(單例模式,堵塞隊(duì)列,定時(shí)器),具有一定參考價(jià)值,加深多線程編程的理解還是很有幫助的,需要的朋友可以參考下2021-08-08使用springBoot中的info等級(jí)通過(guò)druid打印sql
這篇文章主要介紹了使用springBoot中的info等級(jí)通過(guò)druid打印sql,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java并發(fā)底層實(shí)現(xiàn)原理學(xué)習(xí)心得
本片文章是學(xué)習(xí)Java并發(fā)底層實(shí)現(xiàn)原理的一篇知識(shí)心得,對(duì)大家學(xué)習(xí)這個(gè)方便的知識(shí)很有幫助,一起參考下。2018-01-01Spring?Boot中獲取request的三種方式及請(qǐng)求過(guò)程
這篇文章主要介紹了Spring?Boot當(dāng)中獲取request的三種方式,包括請(qǐng)求過(guò)程流程分析及response常用API,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03