Android 壓縮Post請求數據

      Android開發中常會用到Post請求發送數據到服務器,有些情況下Post的數據比較大,比如電子市場獲取本地應用信息,然後將應用包名,版本號發送給服務器,應用一多,xml數據就龐大了,10KB~30KB都有可能。這是壓縮Post的數據就很有必要了。
當然,我們用別的消息格式,如protobuf等效率較高的數據格式也能減少發送的數據,但這會增加服務器和客戶端開發人員的工作量,還要花些時間去了解這種數據交換格式。廢話不多說,看看下面Post消息體的格式(包含了一個文件上傳的Post請求)。

-------------------7d4a6d158c9
Content-Disposition: form-data; name="myfile"; filename="test.txt"

<this is file content>
-------------------7d4a6d158c9
Content-Disposition: form-data; name="text1" 

foo
-------------------7d4a6d158c9
Content-Disposition: form-data; name="text2" 

<this is gzipped post field> gzipped size:214 original:416
-------------------7d4a6d158c9--

在這個請求體內,name爲text2的字段內容採用了gzip壓縮,模擬較大的字符串字段。當字符串上10KB壓縮量還是挺可觀的 ,可減少一倍到四倍的流量。關於怎麼構造這個消息體可以參考這裏特別留意這裏

Content-Disposition: form-data; name="text2"

的空格,冒號和分號間分別有空格,不然服務器解析會出錯,至於爲什麼?RFC規定就這麼地。

下面看看服務器端構造Post消息體(這個是在之前一篇文件上傳的文章上增加的功能)

 

package com.hoot.regx;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class PostData {
	private static final String LONG_STRING = "require 'zlib'equire 'stringio'"
			+ "File.open('t1.gz', 'w') do |f|  gz = Zlib::GzipWriter.new(f)"
			+ "  gz.write 'part one'  gz.closeendFile.open('t2.gz', 'w') do |f|"
			+ "  gz = Zlib::GzipWriter.new(f)  gz.write 'part 2'  gz.close"
			+ "endcontents1 = File.open('t1.gz', \"rb\") {|io| io.read }"
			+ "contents2 = File.open('t2.gz', \"rb\") {|io| io.read }"
			+ "c = contents1 + contents2"
			+ "gz = Zlib::GzipReader.new(StringIO.new(c))" + "gz.each do | l |"
			+ "   puts l" + "end";

	private static final String CHAR_SET = "UTF-8";
	private static final String BOUNDARY = "-----------------7d4a6d158c9";
	private static final String TWO_HYPHENS = "--";
	private static final String END = "\r\n";

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		PostData pd = new PostData();
		pd.uploadFile();
		// pd.uploadXML();
	}

	public void uploadFile() throws IOException {
		URL url = new URL("http://localhost:4567/upload");
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();

		conn.setDoOutput(true);
		conn.setDoInput(true);
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Connection", "Keep-Alive");
		conn.setRequestProperty("Charset", CHAR_SET);
		conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="
				+ BOUNDARY);

		StringBuffer sb = new StringBuffer();
		// 分解符
		sb.append(TWO_HYPHENS + BOUNDARY + END);

		// 設置與上次文件相關信息
		// 上傳文件信息和文件的內容間必須有一個空行,否則會把後面的數據當做屬性讀
		sb.append("Content-Disposition: form-data; name=\"myfile\"; filename=\"test.txt\""
				+ END + END);

		System.out.print(sb.toString());

		byte[] data = sb.toString().getBytes();
		OutputStream os = conn.getOutputStream();
		os.write(data);

		// 一下是文件數據
		FileInputStream fis = new FileInputStream(new File("test.txt"));
		byte[] buf = new byte[1024];
		int len = 0;
		while ((len = fis.read(buf)) > 0) {
			os.write(buf, 0, len);
		}
		System.out.print("<this is file content>");

		/**
		 * 注意form-data後面的空格 -----------------------------7d33a816d302b6
		 * 
		 * Content-Disposition: form-data; name="text1"
		 * 
		 * foo -----------------------------7d33a816d302b6
		 */
		String split = END + TWO_HYPHENS + BOUNDARY + END
				+ "Content-Disposition: form-data; " + "name=\"text1\" " + END
				+ END + "foo"/* + END */;
		System.out.print(split);
		os.write(split.getBytes());

		byte[] b = ZipUtil.compress(LONG_STRING.getBytes());

		split = END + TWO_HYPHENS + BOUNDARY + END
				+ "Content-Disposition: form-data; " + "name=\"text2\" " + END
				+ END/* + dataStr + END */;
		System.out.print(split);
		os.write(split.getBytes());
		os.write(b);
		os.write(END.getBytes());
		System.out
				.print("<this is gzipped post field> gzipped size:" + b.length
						+ " original:" + LONG_STRING.getBytes().length + END);

		String endStr = TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + END;
		byte[] end_data = endStr.getBytes();

		System.out.print(endStr);
		os.write(end_data);
		os.flush();
		os.close();
		fis.close();

		InputStream is = conn.getInputStream();
		while ((len = is.read(buf)) > 0) {
			System.out.write(buf, 0, len);
		}
		// is.close();
	}

}

以下是壓縮字符串的工具類

public static byte[] compress(byte[] data) throws IOException {
		if (data == null || data.length == 0) {
			return data;
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		GZIPOutputStream gzip = new GZIPOutputStream(out);
		gzip.write(data);
		gzip.close();
		return out.toByteArray();
	}

服務器端還是sinatra框架,小巧實用。如果你Servlet順手,那也無所謂,好久沒搞Java EE這套了,忘得差不多了

require 'rubygems'
require 'sinatra'
require 'haml'
require 'zlib'

get '/' do
  'Hello world'
end

# Handle GET-request (Show the upload form)
get "/upload" do
  haml :upload
end

# Handle POST-request (Receive and save the uploaded file)
post "/upload" do
  logger.info "#{params}"
  unless   params[:myfile] &&
  (tmpfile = params[:myfile][:tempfile]) &&
  (name = params[:myfile][:filename])
    @error = "No file selected"
    logger.info "params #{@error} file: #{tmpfile} name: #{name} #{params}"
    return haml(:error)
  end
  directory = 'uploads'
  path = File.join(directory, name)
  logger.info "name:#{params[:text1]}, #{params[:text2]}"

  #gz = Zlib::GzipReader.new(params[:text2])
  #print gz.read
  #gz.close
  logger.info inflate(params[:text2])

  File.open(path, "wb") do |f|
    f.write(tmpfile.read)
  end
  @msg = "file  #{name} was successfully uploaded!"
end

def inflate(string)
  gz = Zlib::GzipReader.new(StringIO.new(params[:text2].force_encoding("UTF-8")))
  data = gz.read
end

 博文源地址這裏,PS:沒想到這邊閱讀量這麼高。。。我可憐的VPS上的博客。。。。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章