12月11, 2017

使用iframe无刷新上传文件

如果需要无刷新提交数据,一般来说我们都会使用AJAX来实现,AJAX的强大是毋庸置疑的。特别是在XMLHttpRequest Level 2中,AJAX进一步得到扩充,可以使用AJAX来上传文件,并且可以监控上传进度,同时还可以对大文件进行分片上传。

MLHttpRequest Level 2上传文件固然好用,但也还是有其局限性——万恶的兼容性。因为是比较新的技术,所以想在比较低的浏览器(比如IE)使用是完全行不通的。

这时候我们只能使用表单来上传文件,表单提交有一个特点就是会导致页面跳转,这显然也是不友好的。可以配合一个隐藏的iframe来实现表单的无刷新提交。具体原理是将表单的提交目标(target属性)设置为一个隐藏的iframe,使表单提交到这个iframe中,同时监控该iframe的onload事件,表单提交完成后从iframe中或取提交结果。

直接给出DEMO代码。

表单页面

<div class="form">
    <form action="http://localhost:9090/upload" method="post" target="upload-iframe" enctype="multipart/form-data">
        <input type="file" name="file">
        <button type="submit">
            提交
        </button>
    </form>
</div>
<div class="img"></div>
<iframe name="upload-iframe" style="display:none"></iframe>
<script>
    document.querySelectorAll('iframe')[0].addEventListener('load', function(e){
        const doc = this.contentWindow.document;
        const path = (JSON.parse(doc.body.innerText) || {}).path;
        var img = document.createElement('img');
        img.style = 'width:300px;';
        img.src = path;
        document.querySelectorAll('.img')[0].append(img);
    });
</script>

服务端保存图片

const express = require('express');
const app = express();
const path = require('path');
const multer = require('multer');
const upload = multer({
    storage: multer.diskStorage({
        destination: function (req, file, cb) {
            cb(null, path.resolve(__dirname, 'files/uploads'));
        },
        filename: function (req, file, cb) {
            cb(null,  Date.now() + '.' + file.mimetype.split('/').reverse()[0] );
        }
    })
});

app.use(express.static(path.resolve(__dirname, 'files')));

app.post("/upload", upload.single('file'), function(req, res, next) {
    res.send({
        'path': '/uploads/' + req.file.filename
    });
});

app.get('/', function (req, res) {
    res.sendFile(path.resolve(__dirname, 'upload.html'));
});

app.listen(9090, function () {
    console.log('Upload app listening on port 9090!');
});

需要注意,表单不能直接打开,得运行在服务器环境下,不然会报跨域的错误:

Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.at HTMLIFrameElement.<anonymous> (xxxx/upload.html:20:36)

本文链接:https://luodao.me/post/iframe-upload-file.html

-- EOF --

Comments