1. 程式人生 > >用httpUrlConnection實現檔案上傳

用httpUrlConnection實現檔案上傳

1、事先了解

1.1 請求格式

我們使用http來上傳檔案,必須先了解http的請求格式,然後才好發報。主要分為以下四個部分:

1)分界符:由兩個連字元“--”和任意字串組成;

2)標準http報文格式,來形容上傳檔案的相關資訊,包括請求引數名,上傳檔名,檔案型別,接收語言等。

3)上傳檔案的內容,通常是位元組流的形式;

4)檔案全部上傳後的結束符:與分界符類似,只不過需要在分界符後面再加兩個連字元。

1.2 http報文格式

1http請求報文

一個http請求報文格式是由四個部分組成,分別是請求行,請求頭部,空行請求資料。其中需要注意的是空行是必須的。


  1. 請求行

請求行是由請求方法、

URL欄位和HTTP協議版本這個3個部分組成。

請求方法有8個——

GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT

一般常用的就是GET POST 方法。

簡單說下兩者區別——

1GET

絕大部分的HTTP請求報文使用的是GET方法,如get這個單詞本身含義一樣,GET方法是用於獲取查詢資源資訊,當瀏覽器請求一個物件時,使用GET方法。

2POST方法

HTTP客戶機常常在使用者提交表單時使用POST方法,需要用到實體主體。POST表示可能修改變伺服器上的資源的請求

get請求可以獲取靜態頁面,也可以把引數放在URL字串後面,傳遞給

servlet

postget的不同之處在於post的引數不是放在URL字串裡面,而是放在http請求的正文內。

URL欄位就是"http://192.168.1.110:8080/abc"這種瀏覽器訪問地址。

HTTP協議版本就是1.0 或者1.1 這兩種其中之一。

  1. 請求頭部


2Android上傳程式碼

httpUrlConnection URLConnection的子類,我們先來看看 URLConnection這個抽象類的相關介紹。注:以下內容來自 JDK API 1.6

抽象類URLConnection 是所有類的超類,它代表應用程式和 URL 之間的通訊連結。此類的例項可用於讀取和寫入此

URL 引用的資源。通常,建立一個到 URL 的連線需要幾個步驟:

openConnection()

connect()

對影響到遠端資源連線的引數進行操作。

與資源互動;查詢頭欄位和內容。

---------------------------->

時間

  1. 通過在 URL 上呼叫 openConnection 方法建立連線物件。
  1. 處理設定引數和一般請求屬性。
  2. 使用 connect 方法建立到遠端物件的實際連線。
  1. 遠端物件變為可用。遠端物件的頭欄位和內容變為可訪問。

使用以下方法修改設定引數:

  • setAllowUserInteraction
  • setDoInput
  • setDoOutput
  • setIfModifiedSince
  • setUseCaches

使用以下方法修改一般請求屬性:

  • setRequestProperty

使用setDefaultAllowUserInteraction setDefaultUseCaches 可設定 AllowUserInteraction UseCaches引數的預設值。

上面每個set 方法都有一個用於獲取引數值或一般請求屬性值的對應 get 方法。適用的具體引數和一般請求屬性取決於協議。

在建立到遠端物件的連線後,以下方法用於訪問頭欄位和內容:

  • getContent
  • getHeaderField
  • getInputStream
  • getOutputStream

某些頭欄位需要經常訪問。以下方法:

  • getContentEncoding
  • getContentLength
  • getContentType
  • getDate
  • getExpiration
  • getLastModifed

提供對這些欄位的便捷訪問。getContent方法使用 getContentType 方法以確定遠端物件型別;子類重寫 getContentType 方法很容易。

一般情況下,所有的預連線引數和一般請求屬性都可忽略:預連線引數和一般請求屬性預設為敏感值。對於此介面的大多數客戶端而言,只有兩個需要的方法:getInputStreamgetContent,它們通過便捷方法映象到 URL 類中。

API上對 HttpURLConnection 的介紹是:

每個HttpURLConnection 例項都可用於生成單個請求,但是其他例項可以透明地共享連線到 HTTP 伺服器的基礎網路。請求後在HttpURLConnection InputStream OutputStream 上呼叫 close() 方法可以釋放與此例項關聯的網路資源,但對共享的持久連線沒有任何影響。如果在呼叫 disconnect() 時持久連線空閒,則可能關閉基礎套接字。

因此,由上可知,HttpURLConnectionjava用於特定的HTTP通訊的類。通過使用HttpURLConnection物件的方法設定相關引數,我們就能進行http通訊。

1public void setDoInput(boolean doinput)方法

如果打算使用 URL 連線進行輸入,則將 DoInput 標誌設定為 true;如果不打算使用,則設定為 false。預設值為true。

2public void setDoOutput(boolean dooutput)

將此 URLConnection doOutput 欄位的值設定為指定的值。

URL 連線可用於輸入和/或輸出。如果打算使用 URL 連線進行輸出,則將 DoOutput標誌設定為 true;如果不打算使用,則設定為 false。預設值為 false

3public void setUseCaches(boolean usecaches)

將此 URLConnection useCaches 欄位的值設定為指定的值。

有些協議用於文件快取。有時候能夠進行“直通”並忽略快取尤其重要,例如瀏覽器中的“重新載入”按鈕。如果連線中的UseCaches 標誌為 true,則允許連線使用任何可用的快取。如果為 false,則忽略快取。預設值來自 DefaultUseCaches,它預設為 true

具體程式碼如下:

/* 上傳檔案至Server的方法 */
	private void uploadFile() {
		String end = "\r\n";
		String twoHyphens = "--";
		String boundary = "*****";
		String newName = "image.jpg";
		String uploadFile = "storage/sdcard1/bagPictures/102.jpg";
		;
		String actionUrl = "http://192.168.1.123:8080/upload/servlet/UploadServlet";
		try {
			URL url = new URL(actionUrl);
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			/* 允許Input、Output,不使用Cache */
			con.setDoInput(true);
			con.setDoOutput(true);
			con.setUseCaches(false);
			/* 設定傳送的method=POST */
			con.setRequestMethod("POST");
			/* setRequestProperty */
			con.setRequestProperty("Connection", "Keep-Alive");
			con.setRequestProperty("Charset", "UTF-8");
			con.setRequestProperty("Content-Type",
					"multipart/form-data;boundary=" + boundary);
			/* 設定DataOutputStream */
			DataOutputStream ds = new DataOutputStream(con.getOutputStream());
			ds.writeBytes(twoHyphens + boundary + end);
			ds.writeBytes("Content-Disposition: form-data; "
					+ "name=\"file1\";filename=\"" + newName + "\"" + end);
			ds.writeBytes(end);
			/* 取得檔案的FileInputStream */
			FileInputStream fStream = new FileInputStream(uploadFile);
			/* 設定每次寫入1024bytes */
			int bufferSize = 1024;
			byte[] buffer = new byte[bufferSize];
			int length = -1;
			/* 從檔案讀取資料至緩衝區 */
			while ((length = fStream.read(buffer)) != -1) {
				/* 將資料寫入DataOutputStream中 */
				ds.write(buffer, 0, length);
			}
			ds.writeBytes(end);
			ds.writeBytes(twoHyphens + boundary + twoHyphens + end);
			/* close streams */
			fStream.close();
			ds.flush();
			/* 取得Response內容 */
			InputStream is = con.getInputStream();
			int ch;
			StringBuffer b = new StringBuffer();
			while ((ch = is.read()) != -1) {
				b.append((char) ch);
			}
			/* 將Response顯示於Dialog */
			showDialog("上傳成功" + b.toString().trim());
			/* 關閉DataOutputStream */
			ds.close();
		} catch (Exception e) {
			showDialog("上傳失敗" + e);
		}
	}

3、伺服器端的程式碼


public class UploadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			System.out.println("IP:" + request.getRemoteAddr());
			// 1、建立工廠類:DiskFileItemFactory
			DiskFileItemFactory facotry = new DiskFileItemFactory();
			String tempDir = getServletContext().getRealPath("/WEB-INF/temp");
			facotry.setRepository(new File(tempDir));//設定臨時檔案存放目錄
			// 2、建立核心解析類:ServletFileUpload
			ServletFileUpload upload = new ServletFileUpload(facotry);
			upload.setHeaderEncoding("UTF-8");// 解決上傳的檔名亂碼
			upload.setFileSizeMax(1024 * 1024 * 1024);// 單個檔案上傳最大值是1M
			upload.setSizeMax(2048 * 1024 * 1024);//檔案上傳的總大小限制

			// 3、判斷使用者的表單提交方式是不是multipart/form-data
			boolean bb = upload.isMultipartContent(request);
			if (!bb) {
				return;
			}
			// 4、是:解析request物件的正文內容List<FileItem>
			List<FileItem> items = upload.parseRequest(request);
			String storePath = getServletContext().getRealPath(
					"/WEB-INF/upload");// 上傳的檔案的存放目錄
			for (FileItem item : items) {
				if (item.isFormField()) {
					// 5、判斷是否是普通表單:列印看看
					String fieldName = item.getFieldName();// 請求引數名
					String fieldValue = item.getString("UTF-8");// 請求引數值
					System.out.println(fieldName + "=" + fieldValue);
				} else {
					// 6、上傳表單:得到輸入流,處理上傳:儲存到伺服器的某個目錄中,儲存時的檔名是啥?
					String fileName = item.getName();// 得到上傳檔案的名稱 C:\Documents
														// and
														// Settings\shc\桌面\a.txt
														// a.txt
					//解決使用者沒有選擇檔案上傳的情況
					if(fileName==null||fileName.trim().equals("")){
						continue;
					}
					fileName = fileName
							.substring(fileName.lastIndexOf("\\") + 1);
					String newFileName = UUIDUtil.getUUID() + "_" + fileName;
					System.out.println("上傳的檔名是:" + fileName);
					InputStream in = item.getInputStream();
					String savePath = makeDir(storePath, fileName) + "\\"
							+ newFileName;
					OutputStream out = new FileOutputStream(savePath);
					byte b[] = new byte[1024];
					int len = -1;
					while ((len = in.read(b)) != -1) {
						out.write(b, 0, len);
					}
					in.close();
					out.close();
					item.delete();//刪除臨時檔案
				}
			}
		}catch(FileUploadBase.FileSizeLimitExceededException e){
			request.setAttribute("message", "單個檔案大小不能超出5M");
			request.getRequestDispatcher("/message.jsp").forward(request,
					response);
		}catch(FileUploadBase.SizeLimitExceededException e){
			request.setAttribute("message", "總檔案大小不能超出7M");
			request.getRequestDispatcher("/message.jsp").forward(request,
					response);
	}catch (Exception e) {
			e.printStackTrace();
			request.setAttribute("message", "上傳失敗");
			request.getRequestDispatcher("/message.jsp").forward(request,
					response);
		}
	}

	// WEB-INF/upload/1/3 打散儲存目錄
	private String makeDir(String storePath, String fileName) {
		int hashCode = fileName.hashCode();// 得到檔名的hashcode碼
		int dir1 = hashCode & 0xf;// 取hashCode的低4位 0~15
		int dir2 = (hashCode & 0xf0) >> 4;// 取hashCode的高4位 0~15
		String path = storePath + "\\" + dir1 + "\\" + dir2;
		File file = new File(path);
		if (!file.exists())
			file.mkdirs();
		System.out.println("儲存路徑是"+path);
		return path;
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}

4、實驗結果