超详细的实现上传文件功能教程,文件上传实现。
重要聲明:本文章僅僅代表了作者個(gè)人對(duì)此觀點(diǎn)的理解和表述。讀者請(qǐng)查閱時(shí)持自己的意見進(jìn)行討論。
本文更新不及時(shí),請(qǐng)到原文地址瀏覽:《超詳細(xì)的實(shí)現(xiàn)上傳文件功能教程,文件上傳實(shí)現(xiàn)。》。
一、文件上傳的方式
在程序的世界里,沒有什么功能的實(shí)現(xiàn)方式是單一的。上傳文件也不例外,我們有很多種能夠?qū)崿F(xiàn)文件上傳的方法。但我們最終要采用的,必然是最熟悉、最常用的方法。文件上傳通常有下面的方法進(jìn)行:
這些文件上傳方式,都是基于HTTP協(xié)議的基礎(chǔ),它并沒有強(qiáng)制限定了我們要如何去編碼或處理數(shù)據(jù),相反協(xié)議的存在使世界上所有的程序差異化不是那么太大。HTTP協(xié)議規(guī)定了,提交大量數(shù)據(jù)時(shí),可以使用POST請(qǐng)求來進(jìn)行提交。是的,大多數(shù)后臺(tái)程序都只會(huì)在POST請(qǐng)求下去獲取大量上傳的數(shù)據(jù),而不會(huì)在GET請(qǐng)求下去獲取。你要知道,如果你不規(guī)范提交,當(dāng)然也是可以不規(guī)范的從GET下獲取到提交的數(shù)據(jù)。但極其不建議這樣做。
如果你對(duì)HTTP協(xié)議還不是很了解,你可以通過這篇文章來入門對(duì)HTTP協(xié)議的了解:《【HTTP,Java】認(rèn)識(shí)HTTP協(xié)議,在互聯(lián)網(wǎng)世界里翱翔》。
二、base64 上傳文件
1、HTML網(wǎng)頁內(nèi)使用base64
如果你要在網(wǎng)頁中使用base64的方式將文件提交給后臺(tái),那么你不得不面臨一個(gè)很嚴(yán)峻的問題就是:如何在網(wǎng)頁中獎(jiǎng)文件內(nèi)容讀取并轉(zhuǎn)換成base64的數(shù)據(jù)。很幸運(yùn)HTML5為我們提供了解決方案。
FileReader提供了讀取文件的能力。但它可不是你想象的提供一個(gè)文件路徑就可以讀取了。它能讀取的文件需是來自下面的途徑:
下面以input選擇的文件為例進(jìn)行講解。
a、選擇文件并轉(zhuǎn)換為base64
直接在html文件中寫一個(gè)input并將其type設(shè)定為file即可立即實(shí)現(xiàn)一個(gè)文件選擇功能。現(xiàn)在同時(shí)給出讀取文件的示列代碼:
<body><!-- 文件選擇和上傳按鈕 --><input type="file" id="fileSelecter"><button onclick="uploadFile()">上傳</button><!-- 上傳邏輯 --><script>function uploadFile() {// 找到文件文件選擇框var fileInput = document.querySelector("#fileSelecter");// 獲取選擇的文件// (因?yàn)閕nput是支持選擇多個(gè)文件的,所以獲取文件通過files字段,如果單個(gè)文件也是在這個(gè)files列表里。)var file = fileInput.files.item(0);// 判斷一下if (file == null) {// 沒有選擇文件。就什么都不處理。return;}// 使用FileReader讀取文件。var fileReader = new FileReader();fileReader.addEventListener("error", function (ev) {// 文件讀取出錯(cuò)時(shí),執(zhí)行此方法。// 通過 fileReader.error 可以獲取到錯(cuò)誤信息。});fileReader.addEventListener("load", function (ev) {// 文件讀取成功后調(diào)用此方法。// 通過 fileReader.result 即可獲取到文件內(nèi)容。});fileReader.addEventListener("loadstart", function (ev) {// 讀取開始時(shí)此方法被調(diào)用。});fileReader.addEventListener("loadend", function (ev) {// 文件讀取結(jié)束時(shí)執(zhí)行此方法。// 無論讀取成功,還是讀取失敗。// 總之,在結(jié)束讀文件操作時(shí),此方法都會(huì)調(diào)用。});fileReader.addEventListener("abort", function (ev) {// 文件讀取被中斷時(shí),此方法調(diào)用。// 你可以通過 fileReader.abort() 方法隨時(shí)中斷文件的讀取。});fileReader.addEventListener("progress", function (ev) {// 讀取文件過程不是一次性讀完的,會(huì)進(jìn)行多次讀取。// 沒讀取一次,本方法執(zhí)行一次。});// 將文件內(nèi)容讀取為 base64 內(nèi)容。通過 fileReader.result 即可返回base64的數(shù)據(jù)內(nèi)容。fileReader.readAsDataURL(file);}</script> </body>上述代碼中詳細(xì)注釋了使用FileReader如何讀取文件的使用方式。通過監(jiān)聽load事件,即可獲取到文件讀取的結(jié)果數(shù)據(jù)。如果你將這個(gè)結(jié)果打印出來,你將看到類似下面的數(shù)據(jù):
data:image/jpeg;base64,/9j/4AAQSkZJergxawffwetcwgIKFUEGNiwgefnwkef...KCAcHCg0如果你看到了類似的數(shù)據(jù)。那么你就算是成功一半了。
注意: 其中data:image/jpeg;base64,這一段屬于對(duì)base64數(shù)據(jù)體的描述。文件真實(shí)base64內(nèi)容只有逗號(hào)后面部分。如果后端接口明確告知你只需要傳遞后面的數(shù)據(jù),那么你還需要自行將前面的描述去除。
b、上傳結(jié)果到接口
現(xiàn)在建立請(qǐng)求將得到的結(jié)果提交到服務(wù)器即可。在load監(jiān)聽事件里我們可以成功的獲取到base64的結(jié)果,因此只需要將提交數(shù)據(jù)代碼寫在監(jiān)聽里即可。這里我直接使用JQuery的post方法進(jìn)行數(shù)據(jù)提交。代碼如下:
fileReader.addEventListener("load", function (ev) {// 文件讀取成功后調(diào)用此方法。// 通過 fileReader.result 即可獲取到文件內(nèi)容。var result = fileReader.result;$.post("https://www.microanswer.cn/test/uploadBase64", {base64Data: result}, function (response) {// 服務(wù)器響應(yīng)了我們的上傳請(qǐng)求。}); });如果你對(duì)JQuery的post方法不是很熟悉,請(qǐng)參考:【JQuery】JQuery常用方法總結(jié)、大全。
2、Java程序內(nèi)使用base64
在使用java作為后臺(tái)程序的時(shí)候,有時(shí)候也經(jīng)常通過http向其他服務(wù)器上傳內(nèi)容,那如果是使用base64方式,如何將數(shù)據(jù)以base64提交呢?其實(shí)非常簡單,和上一小節(jié)里提到的BASE64Decoder有點(diǎn)類似,Java里還有一個(gè)類BASE64Encoder,他可以方便的將文件轉(zhuǎn)為一個(gè)base64字符串,看下面的示列代碼:
// 將文件轉(zhuǎn)為base64字符串 public String file2Base64(String filePath) {// 建立文件對(duì)象File file = new File(filePath);BASE64Encoder base64Encoder = new BASE64Encoder();ByteArrayOutputStream out = null;FileInputStream fileInputStream = null;// 標(biāo)準(zhǔn)流讀取代碼模板。try {out = new ByteArrayOutputStream();fileInputStream = new FileInputStream(file);byte[] data = new byte[1024];int datasize = 0;while ((datasize = fileInputStream.read(data)) != -1) {out.write(data, 0, datasize);}out.flush();} finally {try {if (fileInputStream != null) {fileInputStream.close();}}catch (Exception ignore) {}}return base64Encoder.encode(out.toByteArray()); }有了base64字符串,就可以方便的進(jìn)行上傳了。相信后臺(tái)程序一般都會(huì)有一個(gè)封裝好了的網(wǎng)絡(luò)請(qǐng)求工具類,下面示列一個(gè)典型的上傳代碼:
String base64 = file2Base64("D:/temp/test.doc"); HashMap<String, String> param = new HashMap<>(); param.put("base64Data", base64); String response = HttpUtil.postFormUrlEncode("https://www.microanswer.cn/test/uploadBase64", param);此處使用的HttpUtil工具你可以通過下面的maven依賴獲取:
<dependency><groupId>cn.microanswer</groupId><artifactId>HttpUtil</artifactId><version>1.0.1</version> </dependency>3、后臺(tái)接口
上述請(qǐng)求中,將數(shù)據(jù)以 base64Data 字段提交給了接口。現(xiàn)在只需要處理服務(wù)器響應(yīng)后的事情了。先看看接口是如何獲取數(shù)據(jù)的把:
@RequestMapping("/uploadBase64") public Object uploadBase64Data(HttpServletRequest request) throws Exception {String base64Data = request.getParameter("base64Data");// 保存文件后綴名。String fileExtName = "";// 保存文件真實(shí)的base64數(shù)據(jù)String realBase64;// 判斷是否有前綴if (base64Data.startsWith("data:") && base64Data.contains("base64,")) {String[] typeAndData = base64Data.split(",");// 截取 data:image/jpeg;base64, 為=> jpeg;base64,String tempType = typeAndData[0].split("/")[1];// 拿到 ; 號(hào)前面的內(nèi)容作為文件后綴。fileExtName = "." + tempType.substring(0, tempType.indexOf(";"));// 拿到真實(shí)base64數(shù)據(jù)。realBase64 = typeAndData[1];} else {// 沒有前綴,則認(rèn)為所有字符串都是base64實(shí)際內(nèi)容。realBase64 = base64Data;}// 對(duì) base64 字符串進(jìn)行原數(shù)據(jù)讀取byte[] bytes = new BASE64Decoder().decodeBuffer(realBase64);// 構(gòu)建文件將數(shù)據(jù)保存File file = new File("D:/temp/" + UUID.randomUUID().toString() + fileExtName);FileOutputStream fo = new FileOutputStream(file);// 輸出文件內(nèi)容fo.write(bytes);fo.flush();fo.close();// 返回成功JSONObject object = new JSONObject();object.put("code", 200);object.put("msg", "上傳成功");return object; }三、form表單上傳文件
對(duì)于前端來說,這種方式無疑是最簡單的上傳方式,因?yàn)樯踔量梢宰龅讲粚懸稽c(diǎn)JavaScript代碼就實(shí)現(xiàn)了。為了對(duì)前端上傳文件有更深入了解,使用form表單上傳時(shí)也有兩種上傳方案,一種是直接使用form節(jié)點(diǎn)的,另一種則是通過FromData對(duì)象來完成。
1、使用html的form節(jié)點(diǎn)
下面直接展示示列代碼:
<form method="post"action="https://www.microanswer.cn/test/uploadFile"enctype="multipart/form-data">請(qǐng)選擇文件:<input type="file" name="fileData"><br>請(qǐng)輸入文件描述:<input type="text" name="fileDescription"><br><input type="submit" value="提交"> </form>這樣,前端就實(shí)現(xiàn)了文件上傳了。提交給接口uploadFile要怎么去獲取里面的文件,前端不用管了。
2、使用 FormData 對(duì)象
當(dāng)使用FormData完成文件上傳時(shí),事情就變得稍微復(fù)雜一點(diǎn)了。但是有它的優(yōu)勢(shì)。直接在頁面上使用form表單會(huì)造成頁面的跳轉(zhuǎn),上傳成功后就不會(huì)再停留在當(dāng)前頁面了。而使用 FormData 進(jìn)行文件上傳則可以完成異步上傳,達(dá)到不跳頁面的效果。
首先要清除一點(diǎn),FormData對(duì)象要提交數(shù)據(jù)時(shí),其文件數(shù)據(jù)是來自于<input type="file">選擇的文件。加入現(xiàn)在你已經(jīng)選好了文件并且拿到了file對(duì)象,那么下面示列一個(gè)如何將此數(shù)據(jù)上傳到服務(wù)端的代碼:
// 文件上傳方法。 callback 是上傳完成回調(diào)。 function uploadFile(file, callback) {var formData = new FormData();formData.append("fileData", file); // 添加文件到 formData。formData.append("fileDescription", "文件上傳描述信息。"); // 添加一個(gè)描述字段。// 進(jìn)行上傳$.ajax({url: "https://www.microanswer.cn/test/uploadFile",type: 'post',data: formData,contentType: false, // 不指定contentType,這樣讓JQuery主動(dòng)識(shí)別processData: false, // 不讓JQuery處理上傳的數(shù)據(jù),dataType: 'json', // 預(yù)期返回?cái)?shù)據(jù)格式。success: function (response) {callback(response);},error: function (xmlHttpRequest, statusStr, exception) {callback(undefined, exception);}}); }代碼中可以看到,首先使用 FormData 創(chuàng)建了一個(gè)實(shí)例,然后將文件和描述信息放入其中,最后通過 JQuery 的 ajax 方法將數(shù)據(jù)提交給后臺(tái)接口。
四、使用Java模擬表單
java里沒有類似formdata的上傳文件輔助類,我們需要根據(jù)表單在上傳文件時(shí)數(shù)據(jù)具體是如何進(jìn)行提交的流程進(jìn)行自己使用java來實(shí)現(xiàn)。擺在我們面前的首要任務(wù)就是要搞明白表單提交的數(shù)據(jù)結(jié)構(gòu),只有詳細(xì)了解了其結(jié)構(gòu),才能使用Java進(jìn)行完美的模擬。
1、文件上傳的數(shù)據(jù)內(nèi)容格式
通過表單上傳文件顯然是HTTP協(xié)議內(nèi)的一部分內(nèi)容,因此我們不妨直接翻開HTTP協(xié)議里針對(duì)表單上傳文件相關(guān)的定義文檔:RFC1867。下面是一些重點(diǎn)內(nèi)容的截取部分(博主能力有限,翻譯得不好還請(qǐng)將就湊合):
multipart/form-data的定義
數(shù)據(jù)格式內(nèi)容舉例
2、Java代碼實(shí)現(xiàn)表單
通過上述HTTP協(xié)議定義文檔中的描述,相信文件表單在數(shù)據(jù)提交過程中數(shù)據(jù)傳輸方式和格式已經(jīng)有了大致的了解。現(xiàn)在是時(shí)候使用Java實(shí)現(xiàn)這樣一個(gè)功能了。
a、單個(gè)表單文件單元
根據(jù)定義,一個(gè)input可以支持選擇多個(gè)文件的,而在傳輸時(shí),每個(gè)文件的傳輸需要提供一些基本信息。因此,直接為單個(gè)文件單元實(shí)現(xiàn)為一個(gè)類:
/*** InputFile.java* 用于放在表單 Input 里的文件單元。*/ public class InputFile {// 保存當(dāng)前需要傳遞的文件引用。private File file;// 如果要傳遞的數(shù)據(jù)直接是二進(jìn)制數(shù)據(jù)了,這里也提供一個(gè)變量,使支持二進(jìn)制數(shù)據(jù)直接傳遞。private byte[] byteData;// 在不清楚上傳的數(shù)據(jù)是什么,但確切知道上傳的內(nèi)容是一個(gè)inputstream里的內(nèi)容。這里也提供一個(gè)變量,使支持輸入流直接傳遞。private InputStream inputStream;public InputFile(File file) { this.file = file; }// 對(duì)于數(shù)組,不要直接引用,而是取一份其拷貝的數(shù)據(jù)。public InputFile(byte[] byteData) {this.byteData = new byte[byteData.length];System.arraycopy(byteData, 0, this.byteData, 0, byteData.length);}public InputFile(InputStream inputStream) { this.inputStream = inputStream; }// 當(dāng)進(jìn)行提交時(shí),使用此方法,此方法將會(huì)把當(dāng)前單元持有的[文件\數(shù)據(jù)\流]進(jìn)行上傳。public void submit(OutputStream outputStream) throws Exception{// 輸出是,首先建立基本信息輸出。StringBuilder baseInfo = new StringBuilder();if (file != null) {// 有文件,則輸出文件。baseInfo.append(" filename=\"").append(file.getName()).append("\"").append(Constant.LINE_SEPARATOR);baseInfo.append("Content-Type: ").append(new MimetypesFileTypeMap().getContentType(file)).append(Constant.LINE_SEPARATOR);baseInfo.append(Constant.LINE_SEPARATOR);inputStream = new FileInputStream(file);} else {// 輸出數(shù)據(jù) 或 流,但因?yàn)椴恢烂Q,因此填寫一個(gè)隨機(jī)名稱。baseInfo.append(" filename=\"").append(UUID.randomUUID().toString().replaceAll("-", "")).append("\"").append(Constant.LINE_SEPARATOR).append("Content-Type: application/octet-stream").append(Constant.LINE_SEPARATOR).append(Constant.LINE_SEPARATOR);}outputStream.write(baseInfo.toString().getBytes(Constant.charset));// 將具備的流信息輸出。if (inputStream != null) {byte[] datas = new byte[1024];int datasize = 0;while ((datasize = inputStream.read(datas))!= -1) {outputStream.write(datas, 0, datasize);}if (file != null) {inputStream.close();}} else if (byteData != null && byteData.length > 0) {// 將數(shù)據(jù)輸出。outputStream.write(byteData);}outputStream.write(Constant.LINE_SEPARATOR.getBytes(Constant.charset));} }b、Input單元
input單元里可以放置一個(gè)鍵值對(duì),用來傳遞數(shù)據(jù),同時(shí),它還可以傳遞鍵和一系列文件,上一節(jié)的文件單元就可以放在這個(gè)Input單元中。則可以設(shè)計(jì)出 Input單元類如下:
/*** Input.java* 表單里面的一條input輸入單元。*/ public class Input {// 表單支持的類型枚舉public enum Type { text,file }// 此 input 要提交的name值。private String name;// 此 input 要提交的value值。當(dāng)type為text時(shí)才會(huì)提交此數(shù)據(jù)。private String value;// 此 input 要提交的文件。當(dāng)type為file時(shí)才會(huì)提交此數(shù)據(jù)。private ArrayList<InputFile> inputFiles;// 此表單的類型,目前只有:text 和 fileprivate Type type;// 允許直接使用鍵值對(duì)構(gòu)造一個(gè)text的input。public Input(String name, String value) {this.name = name;this.value = value;this.type = Type.text;}// 允許使用name和文件列表構(gòu)造一個(gè)file的input。public Input(String name, InputFile... inputFile) {this.name = name;this.inputFiles = new ArrayList<>();this.inputFiles.addAll(Arrays.asList(inputFile));this.type = Type.file;}// 在提交前,允許添加文件。public Input addFile(InputFile inputFile) {this.inputFiles.add(inputFile);return this;}// 在提交前,允許修改要提交的值。public Input setValue(String value) {this.value = value;return this;}// 將此input內(nèi)的數(shù)據(jù)進(jìn)行提交。public void submit(OutputStream outputStream) throws Exception {// 先構(gòu)建基本信息輸出。StringBuilder baseInfo = new StringBuilder();baseInfo.append("content-disposition: form-data; name=\"").append(name).append("\";");if (type == Type.text) {baseInfo.append(Constant.LINE_SEPARATOR);baseInfo.append(Constant.LINE_SEPARATOR);baseInfo.append(value);baseInfo.append(Constant.LINE_SEPARATOR);String s = baseInfo.toString();outputStream.write(s.getBytes(Constant.charset));} else {if (inputFiles != null && inputFiles.size() > 0) {if (inputFiles.size() == 1) {outputStream.write(baseInfo.toString().getBytes(Constant.charset));inputFiles.get(0).submit(outputStream);} else {String boundary = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 6);// 此input有多個(gè)文件。先輸出內(nèi)容體的基本信息。baseInfo.append(Constant.LINE_SEPARATOR);baseInfo.append("Content-Type: multipart/mixed, boundary=").append(boundary);baseInfo.append(Constant.LINE_SEPARATOR);baseInfo.append(Constant.LINE_SEPARATOR);// 循環(huán)輸出文件內(nèi)容for (InputFile infile: inputFiles) {baseInfo.append("--").append(boundary).append(Constant.LINE_SEPARATOR);baseInfo.append("content-disposition: attachment;");outputStream.write(baseInfo.toString().getBytes(Constant.charset));infile.submit(outputStream);baseInfo = new StringBuilder();}// 追加上最后一個(gè) boundary。baseInfo.append("--").append(boundary).append("--").append(Constant.LINE_SEPARATOR);outputStream.write(baseInfo.toString().getBytes(Constant.charset));}}}} }c、 form 表單
實(shí)現(xiàn)了 Input 之后,最后一個(gè)便是form實(shí)現(xiàn)了,form只需要實(shí)現(xiàn)其基本的數(shù)據(jù)組裝即可完成:
/*** Form.java* 模擬HTML頁面表單行為。*/ public class Form {private ArrayList<Input> inputs;public Form() {this.inputs = new ArrayList<>();}// 往表單中添加一個(gè)input數(shù)據(jù)。public Form addInput(Input input) {this.inputs.add(input);return this;}// 提交這個(gè)表單。傳入你要提交到的目標(biāo)地址。// 此處代碼比較簡單,可以按需自己實(shí)現(xiàn)或修復(fù)其中存在的問題。public String submit(String url) throws Exception {if (inputs == null || inputs.size() == 0) {throw new NullPointerException("請(qǐng)至少添加一個(gè)要提交的表單數(shù)據(jù)。");}URL url1 = new URL(url);URLConnection urlConnection = url1.openConnection();HttpURLConnection c = (HttpURLConnection) urlConnection;// 設(shè)置請(qǐng)求方式、c.setRequestMethod("POST");c.setDoOutput(true);c.setDoInput(true);String boundary = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 6);// 設(shè)置headerc.setRequestProperty("Content-Type", "multipart/form-data, boundary=" + boundary);// 進(jìn)行傳輸。OutputStream outputStream = c.getOutputStream();for (Input in : inputs) {outputStream.write(("--" + boundary + Constant.LINE_SEPARATOR).getBytes(Constant.charset));in.submit(outputStream);}outputStream.write(("--" + boundary + "--").getBytes(Constant.charset));outputStream.flush();// 獲取結(jié)果InputStream inputStream = c.getInputStream();// 讀取結(jié)果字符串。InputStreamReader reader = new InputStreamReader(inputStream, Constant.charset);char[] chars = new char[256];int charsize= 0;StringBuilder result = new StringBuilder();while ((charsize = reader.read(chars))!= -1) {result.append(chars, 0, charsize);}c.disconnect();return result.toString();} }d、測(cè)試結(jié)果
現(xiàn)在InputFile、Input、Form 類都已完成開發(fā),不妨進(jìn)行一下測(cè)試:
// 構(gòu)建一個(gè)表單 Form form = new Form();// 加入一個(gè)普通的鍵值對(duì)。 form.addInput(new Input("name", "Jack"));// 加入一個(gè)包含多個(gè)文件的input。 form.addInput(new Input("fileData",new InputFile(new File("C:\\Users\\Micro\\Desktop\\file1.jpg")),new InputFile(new File("C:\\Users\\Micro\\Desktop\\file2.jpg")) ));// 提交到指定地址。 String result = form.submit("https://www.microanswer.cn/test/uploadFile");// 打印結(jié)果 System.out.println("提交結(jié)果:" + result);// 輸出:提交結(jié)果:{"msg":"success","code":200,"data":null}完美通過。
e、附加類
在上述幾個(gè)類中,使用了一個(gè)常量類,其代碼如下:
public class Constant {/*** 表單中上傳文件,使用的換行必須是 \r\n。*/public static final String LINE_SEPARATOR = "\r\n";/*** 表單中字符串的編碼。*/public static final Charset charset = StandardCharsets.UTF_8;}五、后臺(tái)接收表單文件
本文主要講了Java后臺(tái)如何實(shí)現(xiàn)獲取表單內(nèi)的數(shù)據(jù),下面將分別對(duì)spring后臺(tái)和原生的servlet后臺(tái)進(jìn)行示例。
1、Spring后臺(tái)
如果你的后臺(tái)使用了Spring框架,那么你就幸運(yùn)了,你可以十分方便的拿到表單上傳上來的文件。只需要通過一些簡單的注解就可以完成,下面示列了一個(gè)典型的Controller接口方法,用于獲取表單上傳上來的文件:
@RequestMapping("/uploadFile") public Object uploadFileTest(@RequestParam(value = "fileData") MultipartFile multipartFile,HttpServletRequest request ) throws Exception {// 即可拿到已上傳的文件內(nèi)容。如果你不處理,這個(gè)文件就會(huì)在本次請(qǐng)求結(jié)束時(shí)被刪除InputStream in = multipartFile.getInputStream();return Util.buildReturnJson(WebApplication.Code.SUCCESS, "success", null); }這個(gè)方法針對(duì)input里只有一個(gè)文件時(shí)非常方便。如果某個(gè)input里包含了多個(gè)文件,這個(gè)方法似乎沒法獲取到更多的文件。(如果這個(gè)方法能的法話,還請(qǐng)大佬評(píng)論指正。)
當(dāng)你希望獲取到某個(gè)input下的所有上傳的文件時(shí)。你可以用下面的方法:
@RequestMapping("/uploadFile") public Object uploadFileTest(HttpServletRequest request) throws Exception {// 使用 spring 提供的內(nèi)置工具StandardMultipartHttpServletRequest r = new StandardMultipartHttpServletRequest(request);// 可以直接獲取到input里如果是text類型的值。String name = r.getParameter("name");// 可以通過這樣的方式獲取到某個(gè)input下的所有文件。// 這里就獲取了input的name為files時(shí),提交上來的所有文件。List<MultipartFile> files = r.getMultiFileMap().get("files");return Util.buildReturnJson(WebApplication.Code.SUCCESS, "success", null); }2、servlet 實(shí)現(xiàn)
在servlet中獲取表單上傳的文件相對(duì)比spring里復(fù)雜,但其實(shí)spring只是把servlet的封裝了一下。咱們java后臺(tái)的表單解析功能是自帶就有的。下面給出一個(gè)示例:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws Exception {// 獲取表單的所有 part 內(nèi)容。// 一個(gè) text 的input 是一個(gè) part// 一個(gè) file 的input 里可能有多個(gè)part(因?yàn)榭梢远噙x文件嘛),這些part的name都是 input的name。Collection<Part> parts = request.getParts();Iterator<Part> iterator = parts.iterator();while (iterator.hasNext()) {// 獲取到表單里的每個(gè)part。Part part = iterator.next();part.getName(); // 對(duì)應(yīng)了 input 的 name 屬性。part.getSubmittedFileName(); // 對(duì)應(yīng)了 input=file 時(shí) 提交的文件的真實(shí)名字。(當(dāng)input不是file類型時(shí)此方法返回的null)part.getInputStream(); // 對(duì)應(yīng)了 input=file 是文件時(shí)的文件輸入流。input=text時(shí),其value值也通過此流讀取。// todo 實(shí)現(xiàn)自己的業(yè)務(wù)。} }六、總結(jié)
無論使用什么方式進(jìn)行文件上傳,只要對(duì)HTTP協(xié)議有一定的了解,都可以輕松完成各種需求的開發(fā)。為什么要針對(duì)表單進(jìn)行模擬來上傳文件,因?yàn)檫@是大多數(shù)服務(wù)器的上傳文件方式,許多服務(wù)端也都默認(rèn)支持解析表單文件,因此客戶端的上傳也許迎合服務(wù)器所支持的使用表單進(jìn)行文件上傳。
如果你還對(duì)HTTP協(xié)議不是很了解,可通過這篇文章進(jìn)行入門:《【HTTP,Java】認(rèn)識(shí)HTTP協(xié)議,在互聯(lián)網(wǎng)世界里翱翔》。
總結(jié)
以上是生活随笔為你收集整理的超详细的实现上传文件功能教程,文件上传实现。的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: imgbb图床API
- 下一篇: NEON加速