如何从客户端上传图片到服务器

最近在做项目的过程中,遇到了一个问题,就是如何从安卓端将图片上传到服务器。通过查阅资料后,在此将自己的收获整理总结一下。

HTTP报文的内容

在Web应用程序中,HTTP协议充当了信使的角色。客户端向服务器发起HTTP请求,服务器接收到请求后将所请求的数据写入HTTP响应报文中,再回送给客户端。那么,当向服务器上传一张图片时,HTTP报文是如何将图片“带给”服务器的呢?
首先,我们通过网页表单向服务器发送图片,来看一看HTTP报文的具体内容是什么.
这里我写了一个简单的登录表单:


输入相关数据,点击登录。

通过抓包工具,看一下HTTP报文的内容:

一个完整的HTTP请求包含如下内容:一个请求行,若干请求头,以及实体内容。这里我们主要关注一下请求头和实体内容部分。

在Request Payload中可以看到,实体内容里包含了三部分内容:输入的用户名,密码,以及上传图片的二进制数据。不难发现,它们之间是通过这样一串字符来进行分隔的:
这段字符串的作用正是用来标明一段内容的开始。也就是说,通过它,我们可以将表单中提交的数据分隔开。用户名是用户名部分,图片数据是图片数据部分。那么这段字符串是如何进行设置的呢?这样的实体内容又是如何设置的呢?

我们看一下请求头中的Content-Type字段,它的值为:
没错了,Content-Type字段的作用就是告诉服务器,我传递的数据是什么,你应该以什么方式来接收我传递过去的数据。而它的值一般可以设置为“application/json","application/x-www-form-urlencoded”,“multipart/form-data”等,每种值对应的实体内容的格式都是不一样的。这里的值之所以设置为“multipart/form-data”,是因为它会指定传输的数据为二进制类型,这正好适合我们去传输图片,文件。
再看之后的boundary属性,它的值就是之前的那个字符串。
最后,还有一个地方需要注意一下。在Request Payload中,除了分隔符之外,不同的表单项需要设置的请求头是不同的。如用户名和密码这样的表单项,只需要设置Content-Disposition请求头就可以了。而图片,文件这样的表单项,还需要设置一下Content-Type,目的是表示上传的文件类型。
写到这里,我们可以试想一下,是不是可以在安卓端“仿造”一份这样的报文内容,来进行图片的上传呢?

安卓端设置请求报文

上传部分的代码主要参考了网上大神的一些代码和视频教程,需要注意的点我都标注在代码中了。
代码如下:



public static void uploadFile(File file ,String requesturl){
    String boundary =  UUID.randomUUID().toString();  //通过UUID生成唯一标识符
    
    String PREFIX = "--";
    String LINE_END = "\r\n"; 
    
    try{
      URL url = new URL(requesturl);
      HttpURLConnection conn = (HttpURLConnection)url.openConnection();
      conn.setReadTimeout(TIME_OUT);
      conn.setConnectTimeout(TIME_OUT);
      conn.setDoInput(true); 
      conn.setDoOutput(true); 
      conn.setRequestMethod("POST");//设置请求方式为POST
      conn.setRequestProperty("Charset", "utf-8");//设置编码方式
      conn.setRequestProperty("connection", "keep-alive");//设置持久连接
      conn.setRequestProperty("Content-Type","multipart/form-data" + ";boundary =" + boundary);//这里就是在设置上文中说到的Content-Type请求头
      conn.connect();
      
      if(file != null){
        DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
        StringBuffer sb = new StringBuffer();

        /**
         * 这里开始设置实体内容的格式,首先设置分隔符
         */
        sb.append(PREFIX);
        sb.append(boundary);
        sb.append(LINE_END);
        
        /**
         * 设置Content-Disposition和Content-Type请求头,application/octet-stream是说如果不知道文件的类型,则以二进制流的形式上传下载。
         * 注意回车换行也是必须与报文一致的
         */
        sb.append("Content-Disposition: form-data; name=\"image\"; filename=\""+file.getName()+"\""+LINE_END);
        sb.append("Content-Type: application/octet-stream; charset=utf-8" + LINE_END);
        sb.append(LINE_END);
        
        dos.write(sb.toString().getBytes());

        /**
         * 写入文件的二进制数据
         */
        InputStream in = new FileInputStream(file);
        byte[] buf = new byte[1024];
        int len = -1;
        while((len = in.read(buf)) != -1){
          dos.write(buf,0,len);
        }
        in.close();
        dos.write(LINE_END.getBytes());//在结束符之前,需要再添加一个回车换行
        /**
         * 在boundary后面加上--构成标识符,表示实体内容部分结束了
         */
        dos.write((PREFIX + boundary + PREFIX + LINE_END).getBytes());
        dos.close();
      }
     
    }catch(Exception e){
      e.printStackTrace();
    } 
  }

(PS:这里是Java小萌新一个,第一次写博客,主要是想把自己平时遇到的问题或者是收获,记录下来,理一理自己的思路。同时希望各位大佬指正错误!)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,133评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • 中国的崛起不是人为的偶然,而是时代的必然。虽然大多数中国人没有感觉到,但是中国的的确确已经成为仅次于美国的世界第二...
    香蕉丶BANANA阅读 282评论 0 0
  • 一个对书籍敬而远之的人,不管他怎样刻意的包装,总是难以潇洒起来的,唯有经过书卷的浸润,才有可能超越自我,发消自我。
    魂梦与同阅读 209评论 0 0