Friday 19 September 2014

Asynchronous file upload - PHP & AJAX

 Asynchronous file upload - PHP & AJAX

Asynchronous file uploads is quote popular feature in modern AJAX web-applications. However standard AJAX classes (XmlHttpRequest) does not have capabilities to process or send files selected with "file dialog" (input type="file"). This article contains example application (trivial file-sharing service, like rapidshare, megaupload or yousendit) which uses embedded frames (IFRAME) to upload file. While file is uploaded to hidden frame, user can still access web-page and fill "file description" field.

Compatible 

  1. Adequate versions of Opera, Firefox, Internet Explorer, Safari
  2. PHP 4.3.0 or higher
  3. PHP 5
  4. PHP option 'short_open_tag' switched on (parse errors otherwise)

How IFRAME file uploading works?


    There is a simple <form... which contains only <input type="file" ... > field. Target for this form is a hidden IFRAME (with "display: none;" CSS style) and OnChange event for the file field is set to JavaScript function which checks file extension (optional for this example, but very useful in general) and submits form.
    Special part of the script (marked FILEFRAME, see comments) saves file upload, checks for uploading errors and outputs JavaScript code to that hidden IFRAME. The javascript code uses parent.window.document object, which allows to modify parent document (visible page, which users is viewing). It sets filename value and enables submit button on the other form using getElementById method.
    The other form has 'description' text-field and hidden field 'filename'. User may fill 'description' field while file is uploading. When file uploading is finished, user press submit and "file information" page is generated (based on filename from hidden field and user's file description).

Possible drawback of this method is file garbage: files are uploaded even if user does not press submit button. You may need to write 'garbage file collector' which will delete any unused file.
This example stores all uploaded file in filesystem folder. You need to specify it at the beginning of script, see variables $upload_dir and $web_upload_dir. There is fail-checking which checks whether it is possible to write create files in upload directory.

We use following functions in this example: PHP


    move_uploaded_file - move file uploaded to web server
    fopen - open file
    fwrite - write to opened file
    fclose - close file
    str_replace - replace one substring by another
    filesize - returns file size in bytes
    filemtime - returns file modification time

source code: php


$upload_dir = "/var/www/xxx/yyy"; // Directory for file storing
                                            // filesystem path

$web_upload_dir = "/yyy"; // Directory for file storing
                          // Web-Server dir

/* upload_dir is filesystem path, something like
   /var/www/htdocs/files/upload or c:/www/files/upload

   web upload dir, is the webserver path of the same
   directory. If your upload-directory accessible under
   www.your-domain.com/files/upload/, then
   web_upload_dir is /files/upload
*/


// testing upload dir
// remove these lines if you're shure
// that your upload dir is really writable to PHP scripts$tf = $upload_dir.'/'.md5(rand()).".test";$f = @fopen($tf, "w");
if ($f == false)
    die("Fatal error! {$upload_dir} is not writable. Set 'chmod 777 {$upload_dir}'
        or something like this");fclose($f);unlink($tf);// end up upload dir testing



// FILEFRAME section of the scriptif (isset($_POST['fileframe']))
{
    $result = 'ERROR';
    $result_msg = 'No FILE field found';

    if (isset($_FILES['file']))  // file was send from browser
    {
        if ($_FILES['file']['error'] == UPLOAD_ERR_OK)  // no error
        {
            $filename = $_FILES['file']['name']; // file name
            move_uploaded_file($_FILES['file']['tmp_name'], $upload_dir.'/'.$filename);
            // main action -- move uploaded file to $upload_dir
            $result = 'OK';
        }
        elseif ($_FILES['file']['error'] == UPLOAD_ERR_INI_SIZE)
            $result_msg = 'The uploaded file exceeds the upload_max_filesize directive in php.ini';
        else
            $result_msg = 'Unknown error';

        // you may add more error checking
        // see http://www.php.net/manual/en/features.file-upload.errors.php
        // for details
    }

    // outputing trivial html with javascript code
    // (return data to document)

    // This is a PHP code outputing Javascript code.
    // Do not be so confused ;)
    echo '-';
    echo '';

    exit(); // do not go futher }// FILEFRAME section END



// just userful functions
// which 'quotes' all HTML-tags and special symbols
// from user input function safehtml($s)
{
    $s=str_replace("&", "&", $s);
    $s=str_replace("<", "<", $s);
    $s=str_replace(">", ">", $s);
    $s=str_replace("'", "'", $s);
    $s=str_replace("\"", """, $s);
    return $s;
}

if (isset($_POST['description']))
{
  
    // Let's generate file information page$html =<<{$filename} [uploaded by IFRAME Async file uploader]

{$filename}


This is a file information page for your uploaded file. Bookmark it, or send to anyone...

Date: {$date}

Size: {$size} bytes

Description:
{$description}


download file

back to file uploading

upload-log



Example by Developers Guide
END;
    // save HTML
    $f = fopen($upload_dir.'/'.$filename.'-desc.html', "w");
    fwrite($f, $html);
    fclose($f);
    $msg = "File {$filename} uploaded,
           see file information page";

    // Save to file upload-log
    $f = fopen($upload_dir."/upload-log.html", "a");
    fwrite($f, "
$msg
\n");
    fclose($f);

    // setting result message to cookie 
    setcookie('msg', $msg);
    // redirecting to the script main page
    // we're doing so, to avoid POST form reposting 
    // this method of outputting messages is called 'flash' in Ruby on Rails 
    header("Location: http://".$_SERVER['HTTP_HOST'].$PHP_SELF);
    exit();
    // redirect was send, so we're exiting now}
 // retrieving message from cookie if (isset($_COOKIE['msg']) && $_COOKIE['msg'] != '') 

    if (get_magic_quotes_gpc())
        $msg = stripslashes($_COOKIE['msg']);
    else
        $msg = $_COOKIE['msg'];

    // clearing cookie, we're not going to display same message several times
    setcookie('msg', '');
} ?>

 <!-- Beginning of main page -->
<html><head>
<title>IFRAME Async file uploader example</title>
</head>
<body>
<?php if (isset($msg)) // this is special section for outputing message
    echo '<p style="font-weight: bold;">'.$msg.'</p>';?>
<h1>Upload file:</h1>
<p>File will begin to upload just after selection. </p>
<p>You may write file description, while you file is being uploaded.</p>

<form action="<?=$PHP_SELF?>" target="upload_iframe" method="post" enctype="multipart/form-data">
<input type="hidden" name="fileframe" value="true">
<!-- Target of the form is set to hidden iframe -->
<!-- From will send its post data to fileframe section of
     this PHP script (see above) -->

<label for="file">text file uploader:</label><br>
<!-- JavaScript is called by OnChange attribute -->
<input type="file" name="file" id="file" onChange="jsUpload(this)">
</form>
<script type="text/javascript">
/* This function is called when user selects file in file dialog */
function jsUpload(upload_field)
{
    // this is just an example of checking file extensions
    // if you do not need extension checking, remove
    // everything down to line
    // upload_field.form.submit();

    var re_text = /\.txt|\.xml|\.zip/i;
    var filename = upload_field.value;

    /* Checking file type */
    if (filename.search(re_text) == -1)
    {
        alert("File does not have text(txt, xml, zip) extension");
        upload_field.form.reset();
        return false;
    }

    upload_field.form.submit();
    document.getElementById('upload_status').value = "uploading file...";
    upload_field.disabled = true;
    return true;
}
</script>
<iframe name="upload_iframe" style="width: 400px; height: 100px; display: none;">
</iframe>
<!-- For debugging purposes, it's often useful to remove
     "display: none" from style="" attribute -->

<br>
Upload status:<br>
<input type="text" name="upload_status" id="upload_status"
       value="not uploaded" size="64" disabled>
<br><br>

File name:<br>
<input type="text" name="filenamei" id="filenamei" value="none" disabled>

<form action="<?=$PHP_SELF?>" method="POST">
<!-- one field is "disabled" for displaying-only. Other, hidden one is for
    sending data -->
<input type="hidden" name="filename" id="filename">
<br><br>

<label for="photo">File description:</label><br>
<textarea rows="5" cols="50" name="description"></textarea>

<br><br>
<input type="submit" id="upload_button" value="save file" disabled>
</form>
<br><br>
<a href="<?=$web_upload_dir?>/upload-log.html">upload-log</a>
<br><br><br>

</body>
</html>

If any error or queries drop it in comment section.

0 comments:

Post a Comment