前端处理图片上传的几种方式

用html5实现图片预览功能这篇文章中只是介绍了图片上传过程中预览的实现,那关于图片上传有哪几种方式呢?

最常见的一种就是用表单方式上传,在表单中增加一个input标签,type属性为file。具体代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <form name='test' 
        enctype=multipart/form-data 
        action="./02-index.php" 
        method="post">
        <input type="text" name='wenben' value="12345">
        <input type="file" name="imgfile">
        <input type="submit" value="提交">
  </form>
  <!-- 原生表单方式必须设置   
  enctype=multipart/form-data
  action="./02-index.php" 
  method="post"
  -->
</body>
</html>

这里必须注意的是:form表单上必须设置一个属性,这个属性为:enctype=multipart/form-data。一般我们编写表单时,总是忽略enctype这个属性,这个enctype属性默认值是application/x-www-form-urlencoded,当enctype为application/x-www-form-urlencoded时只适合上传字符串,当上传文件时enctype必须是multipart/form-data。其它两个属性action和method也是必须的,这里不做过多解释。

但是表单上传有个缺点,那就是上传完成后页面会发生跳转,不想发生跳转的话就要用到ajax上传,这里有个坑,我们在上传文本字符串时,通常会直接获取input标签的vlaue值,那大家猜一猜如果我们获取上面代码中input:file的value,结果会是什么呢,结果大家自己去测试,value的值是上传图片的路径,是一个字符串,这个东西传到后端,卵用没有啊。

这里大家要有一个基本认识,上传文件和上传字符串,浏览器的处理方式是完全不同的,enctype=”multipart/form-data”时表示直接将二进制流上传,而enctype=application/x-www-form-urlencoded时表示在发送到服务器之前,所有字符都会进行编码。

那么怎么使用ajax上传图片呢,这里就用到了一个叫做formData的方法。官方是这样解释的:通过FormData对象可以组装一组用 XMLHttpRequest发送请求的键/值对。它可以更灵活方便的发送表单数据,因为可以独立于表单使用。如果你把表单的编码类型设置为multipart/form-data ,则通过FormData传输的数据格式和表单通过submit() 方法传输的数据格式相同,总之就是一句话,可以代替表单上传数据和文件。看一段伪代码:

var formData = new FormData();

formData.append("username", "Groucho");
formData.append("accountnum", 123456); 
// 数字 123456 会被立即转换成字符串 "123456"

// HTML 文件类型input,由用户选择
formData.append("userfile", fileInputElement.files[0]);

var request = new XMLHttpRequest();
request.open("POST", "http://foo.com/submitform.php");
request.send(formData);

字段 "userfile" 包含一个文件。这里有一个坑,那就是向FormData append文件时,append的不是这个input的value,而是input的files[0]属性,字段 "accountnum" 是数字类型,它将被FormData.append()方法转换成字符串类型,FormData 对象的字段类型可以是 File, 或者 string,如果它的字段类型不是File,则会被转换成字符串类型。

我们还可以通过HTML表单创建FormData对象:

var formElement = document.querySelector("form");
var request = new XMLHttpRequest();
request.open("POST", "submitform.php");
request.send(new FormData(formElement));

你还可以在创建一个包含Form表单数据的FormData对象之后和发送请求之前,附加额外的数据到FormData对象里,像这样:

var formElement = document.querySelector("form");
var formData = new FormData(formElement);
var request = new XMLHttpRequest();
request.open("POST", "submitform.php");
formData.append("serialnumber", serialNumber++);
request.send(formData);

这样你就可以在发送请求之前自由地附加不一定是用户编辑的字段到表单数据里。

看完了FormData接着继续咱们的图片上传,看一段代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <form name='test'  >
    <input type="file" name="weqe">
    <input type="submit" value="提交">
  </form>
  <script>
  var input = document.querySelector('input[type=file]');
  var form = document.test;
  form.addEventListener('submit', function(e) {
    e.preventDefault();
      var  fd = new FormData(form);
      var xhr = new XMLHttpRequest();
      xhr.open('post', './02-index.php');
      xhr.send(fd);
  });
  // 原生js实现,直接将原生jsform表单元素塞进ForData构造函数中;此处也不需要设置enctype=multipart/form-data;用formdata构造数据不需要表单的enctype=multipart/form-data属性
</script>
</body>
</html>

我们直接将form元素塞进了FormData里面,而且form表单也不需要设置enctype属性。

再看上面这种方式的另一种变形:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <form name='test'  action="./02-index.php" method="post">
    <input type="file" name="weqe">
    <input type="submit" value="提交">
  </form>

  <script>
    var input = document.querySelector('input[type=file]');
    var form = document.test;
    form.addEventListener('submit', function(e) {
      e.preventDefault();
      var file = input.files[0];
      var fd = new FormData();
      var xhr = new XMLHttpRequest();
      fd.append('file', file);
      xhr.open('post', './02-index.php');
      xhr.send(fd);
    });
    
  </script>
</body>
</html>

FromData的变换样式,不直接将form表单元素塞进FormData里面;而是new 一个FormData,读取input:file元素的 files[0]属性,并通过FormData实例的append方法将其塞入数据里;然后发送;这种方式可以不用设置enctype=multipart/form-data;甚至form表单元素都是多余的;将文件数据通过append塞入formdata里面和 enctype=multipart/form-data无关;

再看一下用jquery的ajax是如何实现的:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <form name='test' id='abc' >
    <input type="file" name="weqe">
    <input type="submit" value="提交">
  </form>
  <script src="https://cdn.bootcss.com/jquery/2.2.0/jquery.min.js"></script>
  <script src="https://cdn.bootcss.com/jquery.form/3.09/jquery.form.min.js"></script>
  <script>
    $('#abc').submit(function(){
      var fd = new FormData(document.querySelector("form"));
        fd.append("wenben", "This is some extra data");
        $.ajax({
          url: "./02-index.php",
          type: "POST",
          data: fd,
          processData: false,  // 不处理数据
          contentType: false   // 不设置内容类型
        });
        return false;
    })
    
  </script>
</body>
</html>

通过jquery上传图片这里的表单可以不用设置enctype=multipart/form-data;但是有两个参数必须设置contentType: false,processData: false。

那如果用jquery.form这个插件呢,看代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <form name='test' id='abc' enctype=multipart/form-data>
   <input type="text" name='wenben' value="11111111111">
    <input type="file" name="weqe">
    <input type="submit" value="提交">
  </form>
  <script src="https://cdn.bootcss.com/jquery/2.2.0/jquery.min.js"></script>
  <script src="https://cdn.bootcss.com/jquery.form/3.09/jquery.form.min.js"></script>
  <script>
    $("#abc").submit(function(){
      $("#abc").ajaxSubmit({
        url: './02-index.php',
        type: 'post',
        success: function ( info ) {
          console.log( info );
        }
      });
      return false
    })
    
  </script>
</body>
</html>

借助jquery-form上传图片不需要formData;但是form必须指定enctype=multipart/form-data综合前面的案例只有用原生表单上传图片和ajax.form插件上传图片时才需要在表单中设置enctype=multipart/form-data;

最后附上:02-index.php文件的内容,大家可以用wamp配置一个虚拟机自己测试一下:

<?php

print_r( $_FILES );

?>

有疑问可给此公众号发送信息。

欢迎转发!

原文发布于微信公众号 - nodejs全栈开发(geekclass)

原文发表时间:2017-11-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券