Tomcat下檔案下載與上傳的簡單實現
阿新 • • 發佈:2019-02-07
實現下載
實現下載需要
- 修改Tomcat中的server.xml
- 修改web.xml
修改server.xml
在<Host> </Host>
中加入(一般在檔案末尾可以找到)
<Context docBase="C://Download" path="/download" reloadable="true" />
其中docBase
項是本地目錄,path
項是訪問目錄
修改web.xml
<servlet>
<servlet-name>default</servlet-name >
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name >listings</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
其中listings
下確保值是true
這時在C://Download
下存放檔案,並開啟相應的地址,就能看見檔案目錄了
實現上傳
實現上傳也是分兩步
- 實現客戶端的上傳
- 實現服務端的處理
實現客戶端的上傳
post請求
我們都知道http常用的請求方式有get
、post
等,其中我們實現檔案上傳,就是模擬表單採用post
方式上傳檔案
首先來看一下post報文的格式,以上傳一張圖片為例
POST/logsys/home/uploadIspeedLog!doDefault.html HTTP/1.1
Accept: text/plain, */*
Accept-Language: zh-cn
Host: 192.168.24.56
Content-Type:multipart/form-data;boundary=【這裡隨意設定】
User-Agent: WinHttpClient
Content-Length: 3693
Connection: Keep-Alive
以上部分是請求的引數,不需要我們完全實現,通過方法傳參的方式自動構建
--【這裡隨意設定】
Content-Disposition: form-data;name="file1";filename="C://E//a.png"
Content-Type:application/octet-stream
<檔案的二進位制資訊>
--【這裡隨意設定】--
這一部分是報文的內容,需要我們通過字串或者位元組流自行拼接,並且格式不能出錯
注意,三處【這裡隨意設定】的部分必須完全相同
程式碼實現
public static void uploadFile(String fileName) {
try {
// 換行符
final String newLine = "\r\n";
//資料分隔線
final String BOUNDARY = "【這裡隨意設定】";//可以隨意設定,一般是用 ---------------加一堆隨機字元
//檔案結束標識
final String boundaryPrefix = "--";
// 伺服器的域名
URL url = new URL("http://localhost:8070/secondary/HandleFile");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 設定為POST情
conn.setRequestMethod("POST");
// 傳送POST請求必須設定如下兩行
conn.setDoOutput(true);
//conn.setDoInput(true);/不必加,預設為true
//conn.setUseCaches(false);//用於設定快取,預設為true,不改也沒有影響(至少在傳輸單個檔案這裡沒有)
// 設定請求頭引數
//關於keep-alive的說明:https://www.kafan.cn/edu/5110681.html
//conn.setRequestProperty("connection", "Keep-Alive");//現在的預設設定一般即為keep-Alive,因此此項為強呼叫,可以不加
//conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows Nt 5.1; SV1)");//用於模擬瀏覽器,非必須
//用於表示上傳形式,必須
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
//這裡是Charset,網上大多都是Charsert???我的天,笑哭。不過好像沒什麼影響...不知道哪位大佬解釋一下
conn.setRequestProperty("Charset", "UTF-8");
//獲取conn的輸出流用於向伺服器輸出資訊
OutputStream out = new DataOutputStream(conn.getOutputStream());
//構造檔案的結構
//寫引數頭
StringBuilder sb = new StringBuilder();
sb.append(boundaryPrefix)//表示報文開始
.append(BOUNDARY)//新增檔案分界線
.append(newLine);//換行,換行方式必須嚴格約束
//固定格式,其中name的引數名可以隨意修改,只需要在後臺有相應的識別就可以,filename填你想要被後臺識別的檔名,可以包含路徑
sb.append("Content-Disposition: form-data;name=\"file\";")
.append("filename=\"").append(fileName)
.append("\"")
.append(newLine);
sb.append("Content-Type:application/octet-stream");
//換行,為必須格式
sb.append(newLine);
sb.append(newLine);
//將引數頭的資料寫入到輸出流中
out.write(sb.toString().getBytes());
System.out.print(sb);
//寫檔案資料(通過資料輸入流)
File file = new File(fileName);
DataInputStream in = new DataInputStream(new FileInputStream(
file));
byte[] bufferOut = new byte[1024];
int bytes = 0;
//每次讀1KB資料,並且將檔案資料寫入到輸出流中
while ((bytes = in.read(bufferOut)) != -1) {
out.write(bufferOut, 0, bytes);
}
in.close();
//寫引數尾
out.write(newLine.getBytes());
System.out.print(new String(newLine.getBytes()));
// 定義最後資料分隔線,即--加上BOUNDARY再加上--。
sb = new StringBuilder();
sb.append(newLine)
.append(boundaryPrefix)
.append(BOUNDARY)
.append(boundaryPrefix)
.append(newLine);
// 寫上結尾標識
out.write(sb.toString().getBytes());
System.out.println(sb);
//輸出結束,關閉輸出流
out.flush();
out.close();
//定義BufferedReader輸入流來讀取URL的響應 ,注意必須接受來自伺服器的返回,否則伺服器不會對傳送的post請求做處理!!這裡坑了我好久
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
System.out.println("傳送POST請求出現異常!" + e);
e.printStackTrace();
}
}
註釋很清楚,結合post請求的格式理解一下,就不一一解釋了
實現服務端的處理
上述的程式碼會形成一個post
請求到伺服器,於是相應的介面的dopost()
方法就會相應
dopost()
中做了什麼呢?
- 獲取報文中的檔案資訊
- 處理檔案資訊,包括識別檔名,識別檔案型別(由使用者定義,而不是檔案字尾)
- 儲存到本地(伺服器端硬碟)
程式碼如下:
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
//注意導的包
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
//輸出到客戶端瀏覽器
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload sup = new ServletFileUpload(factory);//這裡要將factory傳入,否則會報NullPointerException: No FileItemFactory has been set.
try{
List<FileItem> list = sup.parseRequest(request);
for(FileItem fileItem:list){
System.out.println(fileItem.getFieldName()+"--"+fileItem.getName());
if(!fileItem.isFormField()){
if("file".equals(fileItem.getFieldName())){
//獲取遠端檔名
String remoteFilename = new String(fileItem.getName().getBytes(),"UTF-8");
File remoteFile = new File(remoteFilename);
//設定伺服器端存放檔案的位置
File locate = new File("C://E//download/",remoteFile.getName());
// locate.getParentFile().mkdirs();//用於確保檔案目錄存在,如果為單級目錄可以去掉
locate.createNewFile(); //建立新檔案
InputStream ins = fileItem.getInputStream(); //FileItem的內容
OutputStream ous = new FileOutputStream(locate); //輸出
try{
byte[] buffer = new byte[1024]; //緩衝位元組
int len = 0;
while((len = ins.read(buffer))>-1)
ous.write(buffer, 0, len);
}finally{
ous.close();
ins.close();
}
}
}
}
}catch (FileUploadException e){}
out.print("everything is ok");
out.flush();
out.close();
}
同樣註釋很清楚,不再解釋了
小結
將上述過程理解清楚後,再結合post報文的格式,那麼是否就可以處理多個檔案同時上傳了呢?如果對客戶端的流做出一些大小限制,是不是就可以限制上傳大小了?…一個小demo,幫助理解一下基礎的實現。