跨域上传

时间:2011-09-23 05:30:24

标签: file-upload cross-domain

是否有方法使用url中的表单上传文件A在其“action”属性中指向url B而不将页面重定向到url B?

1 个答案:

答案 0 :(得分:0)

我知道这是3年之后,但由于我今天也有同样的问题,我想我会在这里发布我的解决方案。它不是最漂亮的,但它使用iframe,这应该有助于它更好地支持传统浏览器。 iframe的最大挑战是,出于安全原因(浏览器限制所述功能),如果iframe的内容在另一个域上,则无法读取iframe的内容。

从本质上讲,我的解决方案就是:

  1. 用户选择要上传的文件。
  2. 包含文件字段的表单会发送一个针对隐藏的iframe的POST请求。
  3. PHP脚本在处理程序脚本中执行,上传文件。
  4. 当脚本完成(或出错)时,它会发送Location重定向,将iframe发送回HTTP_REFERER,这将与表单页面位于同一个域中,这意味着该脚本将能够读取内容。
  5. 为了沿重定向发送有用的信息,我只需添加到referrer页面的GET参数。这些可以通过JavaScript提取和读取。
  6. HTML:

    <form method="post" action="http://other/website/upload.php" enctype="multipart/form-data">
        <input type="file" name="file" />
    </form>
    <br /><br />
    <img id="avatar" />
    

    JavaScript:

    $('form').submit(function () {
        $('<iframe></iframe>').attr({name: 'iframe', id: 'iframe'}).prependTo($('body')).load(function () {
            var response = JSON.parse(decodeURIComponent(/[\\?&]_response=([^&#]*)/.exec(this.contentWindow.location.href)[1]).replace(/\+/g, ' '));
            if (response.error) {
                alert(response.error);
            } else {
                $('#avatar').attr('src', response.url);
            }
    
            $(this).remove();
        });
        $(this).attr('target', 'iframe');
    });
    
    $('input[name="avatar"]').change(function () {
        $('form').submit();
    });
    

    PHP:

    <?php
    define('MAX_SIZE', 28 * 1024); // 28 KB
    define('UPLOAD_PATH', 'uploads/');
    define('UPLOAD_URI', 'http://other/website/uploads/');
    
    header('Content-Type: application/json');
    header('Access-Control-Allow-Origin: *');
    
    function format_size ($bytes) {
        for ($labels = array ('B', 'KB', 'MB', 'GB', 'TB'), $x = 0; $bytes >= 1024 && $x < count($labels)-1; $bytes /= 1024, ++$x);
        return round($bytes, 2) . ' ' . $labels[$x];
    }
    
    function respond ($message) {
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        $params = array_merge($params, array ('_response' => json_encode($message)));
        header('Location: ' . $_SERVER['HTTP_REFERER'] . '?' . http_build_query($params));
        exit;
    }
    
    if (isset($_FILES['file'])) {
        $file = $_FILES['file'];
        switch ($file['error']) {
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                respond(array ('error' => 'You may not upload a file larger than ' . format_size(MAX_SIZE) . '.'));
                break;
            case UPLOAD_ERR_PARTIAL:
            case UPLOAD_ERR_NO_TMP_DIR:
            case UPLOAD_ERR_CANT_WRITE:
            case UPLOAD_ERR_EXTENSION:
                respond(array ('error' => 'The server could not upload the file.'));
                break;
        }
    
        if ($file['size'] > MAX_SIZE) {
            respond(array ('error' => 'You may not upload a file larger than ' . format_size(MAX_SIZE) . '.'));
        } else if (!in_array($file['type'], array ('image/png', 'image/gif', 'image/jpeg'))) {
            respond(array ('error' => 'The uploaded file must be a PNG, GIF, or JPEG image.'));
        }
    
        $split = explode('.', basename($file['name']));
        $ext = array_pop($split);
        $name = implode('.', array_merge($split, array (uniqid(), $ext)));
        if (!move_uploaded_file($file['tmp_name'], UPLOAD_PATH . $name)) {
            respond(array ('error' => 'The server could not upload the file.'));
        } else {
            respond(array ('url' => UPLOAD_URI . $name));
        }
    }
    ?>