无刷新上传图片/文件的三种方式

无刷新上传图片/文件的三种方式

我们知道,用正常提交form表单的方式上传文件,是会跳转到action指定的上传处理url中的,而无刷新上传体验比form表单直接跳转页面上传的方式好很多,现在做上传文件一般都用无刷新上传,我总结了一下,无刷新上传有以下三种方式:

  • 1、iframe上传(最古老的无刷新上传,在Ajax支持上传文件之前,就用的这种方式,兼容性是最强的,不过现在应该很少人用这种方式了)
  • 2、使用Flash上传(主要用来防止一些低版本浏览器,它们不支持Ajax上传文件,但因为以前看网页视频都需要Flashplayer,所以可以认为基本上所有浏览器都是安装了flash的,都是支持flash的)
  • 3、Ajax模拟form表单上传方式
    1)Ajax直接提交文件
    2)Ajax处理图片后再提交文件
    3)Ajax提交DataUrl数据方式

先说一下对应的后端示例代码在文章最后(这里后端语言用的是php)

1、iframe无刷新上传文件

iframe无刷新上传,其实并不是无刷新,它其实就是form表单跳转上传的,但不是直接在当前页面跳转,而是在一个我们看不见的隐藏的iframe里跳转了,并且后台输出的数据也是在iframe里,我们要做的就是把iframe里的上传结果数据获取到就行了(在ifame里跳转刷新后会触发iframe的onload事件,我们就用这个事件来获取iframe中返回的结果),至于后台的接收方式,因为这本质上就是form表单跳转提交,所以后台接收方式跟普通form表单跳转提交的接收方式一样即可,比如php就用$_FILES接收即可!

<html>
    <head>
        <title>iframeUpload</title>
        <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
        <!--<script src="jquery-3.2.1.js"></script>-->
        <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
        <style>
            #upload-form {
                width: 50%;
                margin: 0 auto;
                border: 1px solid blue;
            }
            .field{
                padding: 10px;
            }
            .submit-btn{
                text-align:center;
                font-size:20px;
            }
            #upload-iframe {
                display: none;
            }
        </style>
    </head>
    <body>
        <form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data" target="upload-iframe">
            <div class="field">
                <input type="file" name="photo" accept="image/*">
            </div>
            <div class="field">
                <input type="text" name="words">
            </div>
            <div class="submit-btn">
                <input type="submit" value="submit">
            </div>
        </form>
        <iframe id="upload-iframe" name="upload-iframe"></iframe>
    </body>
</html>

对应的js(主要是利用iframe的onload事件获取服务器返回的结果,其实上传的时候压根没用到ajax):

$('#upload-iframe').on('load', function (e){
    var responseData = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent;
    if(responseData!=undefined && responseData!=''){
        var data = JSON.parse(responseData);
        console.log(data);
    }
});

要点:

  • 页面中准备一个隐藏的iframe(其实更好的是用js动态生成一个隐藏iframe,上传完成即remove掉)
  • form表单使用一个target属性target="upload-iframe"来指定提交到iframe中(其中target属性的值是iframe的name,并不是id,这个要注意)
  • 要获取返回的数据,只需监听iframe的onload事件,然后用以下语句来获取iframe页面中的数据(即服务器返回的数据)
var responseData = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent;
  • 由于返回的数据是json字符串,需要使用JSON.parse(responseData)方法解析成json对象以便使用

2、Flash无刷新上传文件

曾经由于Ajax在IE浏览器中不支持,基于Flash的SWFUpload文件上传工具被广泛使用,随着技术的发展,IE新版本也已经支持ajax上传文件(IE10以上支持FormData),而且Flash由于安全性问题等原因,终究不如html5的原生支持来的好,现在国内大部分人都使用各种国产双核浏览器了,如360安全浏览器、360极速浏览器、搜狗浏览器、傲游浏览器、2345浏览器等等,双核其中一核即是谷歌内核,而且一般默认都使用这个核,而且这些浏览器更新也方便,现在买电脑也都预装win10,里面预装的肯定是IE11了,不是非常特殊的公司的项目,已经可以不考虑普通低版本IE了,实在要支持的话你用iframe吧,百分百支持所有浏览器。而手机浏览器几乎可以说百分百支持ajax上传了。

根据以下信息,SWFUploader应该是没有人维护了:
SWFUpload在sourceforge上的代码最新的都是2010年的了,stackoverflow上也有人问这个问题,根据回答,SWFUpload项目多半是死掉了,谷歌论坛swfupload forum上的最新提问已经是2015年7月的了:
Xnip2018-09-19_01-17-48.png

目前我知道的,百度的WebUploader还是支持Flash的,但它自动检测,支持ajax的就用ajax,不支持的才用Flash。

所以,除非插件本身自带兼容支持Flash上传,否则没必要自己写这个啦,它已经被时代所抛弃,直接用下面介绍的ajax上传文件吧。

3、Ajax模拟form表单上传文件

1)不处理照片直接上传文件

<html>
    <head>
        <title>AjaxFormDataUpload</title>
        <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
        <!--<script src="jquery-3.2.1.js"></script>-->
        <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
        <style>
            #upload-form {
                width: 50%;
                margin: 0 auto;
                border: 1px solid blue;
            }
            .field{
                padding: 10px;
            }
            .submit-btn{
                text-align:center;
                font-size:20px;
            }
        </style>
    </head>
    <body>
        <form id="upload-form">
            <div class="field">
                <input type="file" name="photo" accept="image/*">
                <span class="upload-progress"></span>
            </div>
            <div class="field">
                <input type="text" name="words">
            </div>
            <div class="submit-btn">
                <input type="button" value="submit">
            </div>
        </form>
    </body>
</html>

对应的js:

$(document).ready(function(){
    $('input[type="button"]').on('click', function(){
        //方式一:用FormData获取form表单中的数据
        /*var form = $('#upload-form').get(0);
        var formData = new FormData(form);
        formData.append('photo', formData.get('photo'));
        formData.append('words', formData.get('words'));*/
        //注意:formData添加数据后,直接console.log()看到还是空对象,需要用.get()方法获取
        console.log(formData.get('words'));

        //方式二:当然,如果你的表单没有用form标签包起来,也可以不用formData的获取方式,直接获取也是可以的
        var file = $('input[name="photo"]').get(0).files[0];
        var words = $('input[name="words"]').val();
        var formData = new FormData();
        formData.append('photo', file);
        formData.append('words', words);

        //多文件上传处理方式(append的name加个中括号即可)
        /*
        let files = $('input[name="photo"]').get(0).files;
        let formData = new FormData();
        for(let i=0; i<files.length; i++){
            let file = files[i];
            if(/image\/(jpeg|png)/.test(file.type)){
                formData.append('photos[]', file);
            }
        }
        */

        $.ajax({
            type:'post',
            url:'./upload.php',
            data: formData,
            //contentType必须为false(否则会默认为:application/x-www-form-urlencoded; charset=UTF-8)
            contentType: false,
            //告诉jquery不要处理数据(否则报错:Uncaught TypeError: Illegal invocation)
            processData: false,
            xhr: function(){
                let xhr = new XMLHttpRequest();
                //监听上传进度事件
                xhr.upload.addEventListener('progress', function (e){
                    if (e.lengthComputable) {
                        let progress = e.loaded / e.total;
                        progress = Math.round(progress * 10000) / 100 + '%';
                        $('.upload-progress').html(progress);
                    }
                },false);
                return xhr;
            },
            success:function(response){
                console.log(response);
            }
        });
        return false;
    });
});

如果没设置processData: false,属性,则直接报错:
Xnip2018-09-14_20-16-31.png
而如果没设置contentType: false,属性,则服务器端(php)打印出来的数据是这样的,也就是说把它当成普通的post数据了:
Xnip2018-09-15_12-35-26.png

它对应的header和formData数据是酱紫的,也就是默认把content-type设置成了application/x-www-form-urlencoded; charset=UTF-8
Xnip2018-09-15_12-56-17.png

正常上传的header及formData(注意我们不能自己指定content-type:"multipart/form-data", 因为你看下图它后面还有boundary=—-WebKitFormBoundaryAWbvv1pkkNgpqrk,这个是自动加的,而我们主动指定content-type:"multipart/form-data",就少了后面那串,肯定就有问题,实际测试也试过是报错的)
Xnip2018-09-15_13-37-35.png

2)处理图片后再提交文件

处理数据包括把iPhone照片Orientation为3/6/8的照片旋转为正的方向,并按给定的最大宽高缩小照片,并且把照片质量设置为原来的80%(即0.8),这样既可以提高上传速度,节省手机流量,又能避免在服务器处理照片增加服务器压力,当然也能做一些裁剪等处理照片的各种操作。

iPhone不同方向拍的照片直接在网页上显示会有四种方向(方向是一个叫Orientation的数值,记录在图片的EXIF信息中,使用):

  • 1)Orientation=1 => 正常方向
  • 2)Orientation=3 => 顺时针旋转了180°(处理时,顺或逆时针旋转180°即可转正)
  • 3)Orientation=6 => 顺时针旋转了270°(也相当于逆时针旋转了90°,即-90°,处理时,顺时针转90°即可转正)
  • 4)Orientation=8 => 顺时针旋转了90°(处理时,逆时针转90°或者顺时针旋转270°即可转正)
    iPhone不同拍照方向对应Orientation值.png

Xnip2018-09-19_11-26-34.png

更深入的查看图片为什么会被旋转的问题请查看:笔记:JavaScript 读取 EXIF 的 Orientation

  • 值得注意的是,以上所说『iPhone不同方向拍的照片直接在网页上显示会有四种方向』这个说法,只有用安卓机浏览器或者电脑浏览器去查看才会有旋转的情况,如果你用iPhone查看(包括iPhone自带的Safari浏览器,Chrome浏览器,UC浏览器、以及小众的Alook浏览器,以及微信和QQ内置浏览器)查看,都不会有旋转的情况,原因是iPhone上的浏览器用的都是Safari的内核,而Safari会把图片自动转正,所以我们在iPhone上看不到这些方向的变化,但是假如我们直接传到服务器,服务器也不处理的话,那么,用安卓机和电脑的浏览器查看你的图片的时候,图片就会有各个方向的。

  • 解决这个问题的办法,当然就是把这些图片给『转正』了,转正有三种处理办法:
    1)第一种方法是在服务器端转正,客户端(即浏览器)不用管,上传的时候显示一个表示上传中的gif图(转圈圈那个),如果做的好一点,那么加个上传进度,用百分数或者用进度条显示,等上传完成,服务器处理完了保存好了图片,返回一个url后,我们再把url插入到DOM中,进而显示出图片,由于图片在服务器端被转正,所以不管你用iPhone,安卓,还是电脑查看这个网页,图片都是正的。
    2)第二种,还是服务器端转正,但是客户端浏览器上,先直接把图片显示出来(用js的FileReader对象的readAsDataURL方法,即可把照片转成DataURL(即我们说的base64),把DataURL直接赋值给img.src,即可显示),再加个上传百分数或者进度条用来显示进度,这样的好处是用户体验会好一点,但是这里我们就必须考虑到图片旋转的问题了:
    ① 如果是在iPhone上打开的网页,上传图片,那么我们不用考虑方向问题,因为Safari会自动转正。虽然iPhone上自动做了转正处理,但如果我们还是用js处理一下转正,图片会反而变成歪的吗?如果用的是canvas转正的,我实测不会,但如果用的是jq插件,它有可能不是用canvas转正图片,而是用css3旋转图片容器,这样的话,在iPhone上反而会变歪!
    ②如果是安卓机,因为安卓拍的照片都没有方向(暂时不考虑据说少数有方向的),所以也不需要旋转,直接读出dataUrl赋值给img.src即可显示。看起来好像是这样的,但是万一这图片是你朋友用iphone通过QQ或者微信发你的呢?有一种说法是QQ、微信会自动处理图片,处理过的图片也是正的,但我实测表明,如果微信如果勾选了发送原图,那么安卓收到的图片是不会被处理的,而如果通过手机QQ发的话,不管勾不勾选原图,都不会处理图片的方向(我不是猜测,我是实际测试过的),这时候如果直接显示,因为是在安卓浏览器上显示iPhone照片,如果你不转正,那看起来肯定就是歪的(除非iPhone发给你的照片刚好是Orientation=1就不会歪),所以我们判断,如果是安卓访问的网页,那就需要做旋转处理,同样,如果是电脑访问,那就更需要处理,相反,如果是iPhone访问这个网页,那就不需要处理,因为前面说过了,iPhone的浏览器都是Safari内核,而Safari会自动把图片转正的。
    3)第三种方式,也像第二种一样,在浏览器显示,安卓机和电脑版都要做旋转处理,但是,我们在上传的时候,不像第二种直接上传图片文件(即二进制文件,我们平时说的文件,其实都是二进制方式存储的),而是把旋转后的DataUrl数据转成二进制文件再传给服务器,这样的话,服务器就不需要做转正处理了(就算做了判断,也会因为收到的图片是orientation不存在,所以不会去旋转它)
    另外,需要注意一下,iPhone好像自从iOS11开始,就有HEIC格式图片,目前我发现iOS12是默认为HEIC格式的(但是可以切换成jpeg:设置相机格式高效),说这个格式,是因为HEIC格式图片直接用formData这样传到后端,拿php来说,本来是用$_FILES来接收文件的,它会有个tmp_name用来存储临时文件,但我测试如果是HEIC格式的,上传后tmp_name是空的,也就是无法上传,而且奇怪的是,我用file.type检测它确实是jpeg(就是说那张图片,如果我用AirDrop直接传到Mac上,它格式是HEIC的,而如果我直接在网页上做上传,js拿到的file.type是jpeg),这样看感觉上传应该是没事的,但事实上就是后台$_FIELS的tmp_name为空,那怎么解决这种情况呢?实测用上面的第三种方式能解决。

所以最后的结论就是,在客户端统一做压缩旋转处理,这样,不管是在iPhone,安卓,还是电脑上都不会有问题。

html直接使用“1)不处理照片直接上传文件”的html,但要添加一个用于获取照片EXIF信息的js

<script src="https://cdn.bootcss.com/exif-js/2.3.0/exif.js"></script>

对应js代码如下:

$(document).ready(function(){
    let orientation = null;
    let model = '';
    $('input[type="button"]').on('click', function(){
        //获取图片文件对象
        let file = $('input[name="photo"]').get(0).files[0];
        let matchArr = file.type.match(/image\/(jpeg|png)/);
        if(!matchArr){
            alert("只允许上传jpg或png格式图片");
            return false;
        }
        let ext = matchArr[1] == 'jpeg' ? 'jpg' : matchArr[1];

        //获取图片的方向信息(用于后面较正图片方向,否则在页面显示可能是旋转了90度或者倒过来的)
        EXIF.getData(file, function(){
            //为什么网上的都要这句getAllTags呢?根本不用啊
            // let tags = EXIF.getAllTags(this);
            orientation = EXIF.getTag(this, 'Orientation');
            // console.log('orientation => ' + orientation);
            model = EXIF.getTag(this, 'Model');
        });

        //指定最大宽高
        let maxWidth = 1050;
        let maxHeight = 1400;
        let quality = 0.8;
        let reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function(e){
            let img = new Image();
            //1、先定义onload事件的回调函数
            img.onload = function(){
                let dstWidth = this.naturalWidth;
                let dstHeight = this.naturalHeight;
                if(maxWidth && maxHeight){
                    //按比例缩放,如果宽大于高(即手机横拍)且宽大于maxWidth,那么宽定为maxWidth,高按比例缩放
                    if(this.naturalWidth > this.naturalHeight && this.naturalWidth > maxWidth){
                        dstWidth = maxWidth;
                        dstHeight = dstWidth * (this.naturalHeight / this.naturalWidth);
                    }
                    //如果高大于宽(即手机竖拍)且高大于maxHeight,那么高定为maxHeight,宽按比例缩放
                    if(this.naturalHeight > this.naturalWidth && this.naturalHeight > maxHeight){
                        dstHeight = maxHeight;
                        dstWidth = dstHeight * (this.naturalWidth / this.naturalHeight);
                    }
                }
                if(maxWidth){
                    //按比例缩放,如果宽大于高(即手机横拍)且宽大于maxWidth,那么宽定为maxWidth,高按比例缩放
                    if(this.naturalWidth > maxWidth){
                        dstWidth = maxWidth;
                        dstHeight = dstWidth * (this.naturalHeight / this.naturalWidth);
                    }
                }
                if(maxHeight){
                    //如果高大于宽(即手机竖拍)且高大于maxHeight,那么高定为maxHeight,宽按比例缩放
                    if(this.naturalHeight > maxHeight){
                        dstHeight = maxHeight;
                        dstWidth = dstHeight * (this.naturalWidth / this.naturalHeight);
                    }
                }

                //把iPhone照片中Orientation为3/6/8的照片旋转为正的方向并把图片尺寸按比例缩小到指定尺寸
                let canvas = rotateImage(this, orientation, dstWidth, dstHeight);
                //把canvas转回dataUrl,第二参数为质量参数(0.1-1,不写或超出范围默认为0.92)
                let dataUrl = canvas.toDataURL('image/jpeg', quality);
                //后台接收到文件后会重命名,所以随便起一个文件名无所谓了
                let fileName = 'tmpImg.' + ext;
                //把dataUrl转回二进制文件
                let newFile = dataURLtoFile(dataUrl, fileName);
                let words = $('input[name="words"]').val();
                //提交文件
                ajaxSubmit(newFile, words);
            }

            //2、再往img对象的src里添加DataUrl数据,img对象加载数据完成后即会触发onload事件
            //(所以这一句放在onload后面,虽然放在onload前面一样会触发onload,但实际上应该是先添加事件,后触发事件)
            //这里this=reader=e.target,所以用哪个都行,但用e.target没有语法提示有哪些属性和方法😂
            img.src = reader.result;
            // console.log(reader.result);
        }
        return false;
    });
});

/**
 * Rotate & scale img to the given size & degree
 * @param img
 * @param orientation
 * @param dstWidth
 * @param dstHeight
 * @returns {HTMLElement}
 */
function rotateImage (img, orientation, dstWidth, dstHeight){
    let canvas = document.createElement('canvas');
    //使用canvas一定要指定宽高,否则会默认为300*150。图片旋转是沿原点转动,因为照片基本上不是正方形的,
    // 只要旋转角度不是180度,旋转后画布的宽高就会交换(希望大家能理解这点),而旋转180度宽度不交换,
    // 但都变成了负数,另外且注意原点向右是x轴正方向(向左则是负值),向下是y轴正方向(向下则是负值),
    // 所以旋转方向要注意这些点。旋转角度正值是顺时针,负值是逆时针,我这里就统一按顺时针做了。

    let tranX = 0;
    let tranY = 0;
    let degree = 0;
    //注意canvas本身的宽高不能为负数,rotate是把整个画布都转动(连坐标轴也转动,这样计算出来的新原点坐标才是对的,
    // 当然也可以直接用公式计算:x’=x*cosθ-y*sinθ,y’=x*sinθ+y*cosθ,x’,y’是转动后的图片的左上角的坐标,
    // x,y是转动前的图片其中一个角的坐标(这个角转动后刚好就是新的左上角x’,y’),θ是要转动的角度,这里我们要转动的
    // 角度分别是是90°/180°/270°)
    switch (orientation){
        //照片逆时针转了90°,我们要顺时针转90°把它转回来
        case 6:
            degree = 90;
            canvas.width = dstHeight;
            canvas.height = dstWidth;
            //顺时针旋转90°,宽高已经交换
            tranX = 0;
            tranY = -dstHeight;
            break;
        //照片旋转了180°,我们要旋转180°把它转回来,因为是180°所以不分顺/逆时针,这里我们统一用顺时针
        case 3:
            degree = 180;
            canvas.width = dstWidth;
            canvas.height = dstHeight;
            tranX = -dstWidth;
            tranY = -dstHeight;
            break;
        //照片顺时针转动了90°,我们要逆时针90°(即-90°)把它转回来,当然也可以用顺时针转动270°,
        //这里我们统一用顺时针,所以我们选择转270°把照片转回正的方向。
        case 8:
            degree = 270;
            canvas.width = dstHeight;
            canvas.height = dstWidth;
            tranX = -dstWidth;
            tranY = 0;
            break;
        //不用转动,放在这里是为了统一处理宽高
        case 1:
        default:
            degree = 0;
            canvas.width = dstWidth;
            canvas.height = dstHeight;
            //顺时针旋转90°,宽高已经交换
            tranX = 0;
            tranY = 0;
    }

    let ctx = canvas.getContext('2d');
    /*
    旋转图片:
    canvas旋转单位是弧度,两条半径从圆心出发到达圆边上,如果两条半径所截取的圆弧长度等于半径长度,
    那么这段圆弧对应的夹角即为1弧度(1rad),圆周长为C=2πr,那么整个圆的弧度数为周长除以半径,
    即2πr/r=2π(rad),则1°等于2π/360°=π/180(rad)
    */
    degree && ctx.rotate(degree*Math.PI/180);
    //drawImage参数解释:参数一为图片对象,参数二、三表示画布原点坐标(即从哪开始画)
    //第四、五个参数表示要画的宽、高
    ctx.drawImage(img, tranX, tranY, dstWidth, dstHeight);
    //把从ctx.save()开始到这里所做的上下文操作(比如移动原点位置等)恢复到原始位置(比如原点就恢复到0,0)
    return canvas;
}

/**
 * Convert DataUrl to file
 */
function dataURLtoFile(dataurl, filename) {
    let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while(n--){
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {type:mime});
}

/**
 * 使用ajax提交数据
 */
function ajaxSubmit (data, words){
    let formData = new FormData();
    formData.append('photo', data);
    formData.append('words', words);
    $.ajax({
        type:'post',
        url:'./upload.php',
        data: formData,
        xhr: function(){
            let xhr = new XMLHttpRequest();
            //监听上传进度事件(为查看效果可以在chrome浏览器的network里开启3G让上传速度慢点)
            xhr.upload.addEventListener('progress', function (e){
                if (e.lengthComputable) {
                    let progress = e.loaded / e.total;
                    progress = Math.round(progress * 10000) / 100 + '%';
                    $('.upload-progress').html(progress);
                }
            },false);
            return xhr;
        },
        //contentType必须为false(否则会默认为:application/x-www-form-urlencoded; charset=UTF-8)
        contentType: false,
        //告诉jquery不要处理数据(否则报错:Uncaught TypeError: Illegal invocation)
        processData: false,
        success:function(response){
            console.log(response);
        }
    });
}

3)提交DataUrl数据方式

『2)处理图片后再提交文件』中以下两句注释掉,ajaxSubmit第一个参数改成dataUrl即可ajaxSubmit(dataUrl, words);,即不转换为文件,直接把dataUrl传给后端,让后端来处理转换成文件:

//后台接收到文件后会重命名,所以随便起一个文件名无所谓了
let fileName = 'tmpImg.' + ext;
//把dataUrl转回二进制文件
let newFile = dataURLtoFile(dataUrl, fileName);

提交DataUrl的话,后端接收文件的方式不再是接收文件,而是接收post的dataUrl字符串,比如php就使用$_POST['dataurl']这样接收,不再是使用$_FILES接收,要注意的几点:

  • 1)接收后要提取出其中的base64字符串,也就是去掉dataUrl的头部信息,如data:image/png;base64,data:image/jpeg;base64,
  • 2)把空格替换成『+』号(因为在传输过程中,base64的+号会变成空格)
  • 3)解码base64并把它写入文件

以上三种上传方式除『3)提交DataUrl数据方式』外简单的后台接收文件的代码(这里用的后台语言是php,只是简单示例,没有做复杂的判断),对于php来说,现在一般都用框架,虽然本质上还是用$_FILES接收和move_uploaded_file()函数把临时文件移动到上传目录,但用框架不需要自己写这个。

<?php
    /*
    var_dump($_FILES);
    var_dump($_POST);
    exit;
    */
    $source = $_FILES['photo']['tmp_name'];
    $destination = __DIR__.'/'.$_FILES['photo']['name'];
    if(move_uploaded_file($source, $destination)){
        echo json_encode(['code'=>0,'msg'=>'上传成功']);
    }else{
        echo json_encode(['code'=>-1,'msg'=>'上传失败']);
    }

以下是『3)提交DataUrl数据方式』的后台简单代码,实际上,如果前端没有做图片的缩小,iPhone照片转正等处理的话,后台还需要做这些事情,一般是利用GD库/ImageMagic(需要服务器安装支持),或者框架自带的一些图片处理类,我这里就不做这些处理。

<?php
    /**
     * Created by PhpStorm.
     * User: bruce
     * Date: 2018-09-18
     * Time: 12:11
     */
    $photoDataUrl = $_POST['photo'];
    $tmpArr = explode(',', $photoDataUrl);
    $base64_content = str_replace(' ', '+', $tmpArr[1]);
    $base64_content = base64_decode($base64_content);
    preg_match('/image\/(jpeg|png)/', $tmpArr[0], $matches);
    $ext = $matches[1]=='jpeg' ? 'jpg' : $matches[1];
    $abs_file_path = __DIR__ . '/'.md5(time().rand(100000, 999999)).'.'.$ext;
    @file_put_contents($abs_file_path,$base64_content);
    if(filesize($abs_file_path) > 0){
        echo json_encode(['code'=>0,'msg'=>'上传成功']);
    }else{
        echo json_encode(['code'=>-1,'msg'=>'上传失败']);
    }

注意:结合php后台代码测试时,打开页面不能直接双击打开,需要有apache/nginx服务器,用http://localhost/AjaxFormDataUpload.htmlhttp://127.0.0.1/AjaxFormDataUpload.html或自己设置域名打开才行。

打赏

订阅评论
提醒
guest

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

无刷新上传图片/文件的三种方式