Java中用Socket实现HTTP文件上传实例
我想做过web开发的程序员大部分都做过文件上传的功能,大多数时候我们都是借助于commons-fileupload这样的jar包实现的。下面我试着通过读取socket的输入流来实现一个文件上传的功能。
在做文件上传之前我们需要先了解一下http post的附件上传协议。http附件上传协议是rfc1876协议,rfc1876协议是在http协议的基础上为input标签增加了file属性,同时限定了form的method必须为post
,enctype
必须为multipart/form-data
。rfc1867协议对http头作了适当地变更,content-type头由以前的:content-type:application/x-www-form-urlencoded变为content-type:multipart/form-data;+空格+boundary=字符串
。rfc1867增加了文件上传得功能,而上传文件内容自然也会被加入到http的实体中。现在因为既有http一般的参数实体,又有上传文件的实体,所以用boundary把每种实体进行了分割。具体的看下图:
接下来就开始我们的代码部分吧。
我在前面的文章中写过创建一个自己的web服务器,现在我们的重点要放在对socket的输入流的解析中。具体代码如下:
public void parserequest() { linenumberreader br = new linenumberreader(new inputstreamreader(inputstream)); stringbuffer sb = new stringbuffer(); string str = null; try { //读取请求行 string requestline = br.readline(); if (!stringutils.isempty(requestline)) { sb.append(requestline); string[] reqs = requestline.split(" "); if (reqs != null && reqs.length > 0) { if ("get".equals(reqs[0])) { method = "get"; } else { method = "post"; } } } //读取请求头 while ((str = br.readline()) != null) { if ("".equals(str)) { break; } if (!stringutils.isempty(str)) { if (str.indexof(":") > 0) { string[] strs = str.split(":"); headers.put(strs[0].tolowercase(), strs[1].trim()); } } sb.append(str).append("\n"); } //post请求,content-type为 multipart/form-data string contenttype = null; if ("post".equals(method) && ((contenttype = headers.get("content-type")) != null && headers.get("content-type").startswith("multipart/form-data"))) { //文件上传的分割位 这里只处理单个文件的上传 string boundary = contenttype.substring(contenttype.indexof("boundary") + "boundary=".length()); //解析消息体 while ((str = br.readline()) != null) { //解析结束的标记 do { //读取boundary中的内容 //读取content-disposition str = br.readline(); //说明是文件上传 if (str.indexof("content-disposition:") >= 0 && str.indexof("filename") > 0) { str = str.substring("content-disposition:".length()); string[] strs = str.split(";"); string filename = strs[strs.length - 1].replace("\"", "").split("=")[1]; system.out.println("filename = " + filename); //这一行是content-type br.readline(); //这一行是换行 br.readline(); //正式去读文件的内容 bufferedwriter bw = null; try { bw = new bufferedwriter(new outputstreamwriter(new fileoutputstream("g:\\learnvideo\\fileload" + file.separator + filename), "utf-8")); while (true) { str = br.readline(); if (str.startswith("--" + boundary)) { break; } bw.write(str); bw.newline(); } bw.flush(); } catch (exception e) { } finally { if (bw != null) { bw.close(); } } } if (str.indexof("content-disposition:") >= 0) { str = str.substring("content-disposition:".length()); string[] strs = str.split(";"); string name = strs[strs.length - 1].replace("\"", "").split("=")[1]; br.readline(); stringbuilder stringbuilder = new stringbuilder(); while (true) { str = br.readline(); if (str.startswith("--" + boundary)) { break; } stringbuilder.append(str); } parameters.put(name, stringbuilder.tostring()); } } while (("--" + boundary).equals(str)); //解析结束 if (str.equals("--" + boundary + "--")) { break; } } } //system.out.println(sb.tostring()); //获取uri uri = stringutils.parseruri(sb.tostring(), " "); int flag = -1; //说明有参数 if ((flag = uri.indexof('?')) >= 0) { string olduri = uri; uri = uri.substring(0,flag); string parameterpath = olduri.substring(flag+1); string[] parameter = parameterpath.split("&"); if (parameter != null && parameter.length > 0) { for (int i = 0; i < parameter.length; i++) { string str1 = parameter[i]; if((flag = str1.indexof('=')) >= 0){ string key = str1.substring(0,flag); string value = str1.substring(flag+1); parameters.put(key,value); }else{ parameters.put(str,null); } } } } } catch (ioexception e) { e.printstacktrace(); } }
我们启动自己创建的web服务器,然后在浏览器中输入:http://localhost:8004/static/uploadpage.html,页面如下:
选择我们要上次的文件,然后点击上传按钮,我们会发现我们的功能已经被上传到g:\learnvideo\fileload这个目录下了。示例如下:
完整的代码请从这里下载:fullstacktraining_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。