I'm trying to build-in an Image Uplaoder to my CMS for the gallery. I've done some research and found what I need to build it. The uploader uses three files. The first one is where to select the images for upload and showing some progress. Connected to this is a js file for resizing the selected images first and upload them afterwards. And last but not least a file to process the images on server via php and for writing data into sql-database.
The good point is: Everything works as it should. BUT I have a problem with sorting the images. Because they are getting a md5 generated filename, and the uploader handles multiple images at a time, some images that I took for example at the end of a day are showed first and the first pictures of the day are for example anywhere between them.
So here comes my question: Is there a way to keep the orignal filename and name the uploaded image for example anything like "1234md5randomdigits_ORIGINALFILENAME.jpg"?
I've tried a lot of $_FILES and other php parameters, but they were empty...
Here is my upload file for selecting images:
<!DOCTYPE html>
<html>
<head>
<title>multiple.php</title>
<link rel="stylesheet" href="./style.css" />
<head>
<body>
<h1>Upload Images...</h1>
<form>
<input type="file" multiple />
<div class="photos">
</div>
</form>
<script src="./upload.js"></script>
</body>
</html>
Here comes the upload.js file
// Once files have been selected
document.querySelector('form input[type=file]').addEventListener('change', function(event){
// Read files
var files = event.target.files;
// Iterate through files
for (var i = 0; i < files.length; i++) {
// Ensure it's an image
if (files[i].type.match(/image.*/)) {
// Load image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Add elemnt to page
var imageElement = document.createElement('div');
imageElement.classList.add('uploading');
imageElement.innerHTML = '<span class="progress"><span></span></span>';
var progressElement = imageElement.querySelector('span.progress span');
progressElement.style.width = 0;
document.querySelector('form div.photos').appendChild(imageElement);
// Resize image
var canvas = document.createElement('canvas'),
max_size = 1200,
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
// Upload image
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// Update progress
xhr.upload.addEventListener('progress', function(event) {
var percent = parseInt(event.loaded / event.total * 100);
progressElement.style.width = percent+'%';
}, false);
// File uploaded / failed
xhr.onreadystatechange = function(event) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
imageElement.classList.remove('uploading');
imageElement.classList.add('uploaded');
imageElement.style.backgroundImage = 'url('+xhr.responseText+')';
console.log('Image uploaded: '+xhr.responseText);
} else {
imageElement.parentNode.removeChild(imageElement);
}
}
}
// Start upload
xhr.open('post', 'process.php', true);
xhr.send(canvas.toDataURL('image/jpeg'));
}
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(files[i]);
}
}
// Clear files
event.target.value = '';
});
And this my "process.php" to process the uploaded data:
<?php
$save_path="/images";
// Generate filename
$filename = md5(mt_rand()).".jpg";
// Read RAW data
$data = file_get_contents("php://input");
// Read string as an image file
$image = file_get_contents("data://".substr($data, 5));
// Save to disk
if ( ! file_put_contents($save_path.$filename, $image)) {
exit();
}
// Clean up memory
unset($data);
unset($image);
//Includes and SQL go after that
// Return file URL
echo $save_path.$filename;
?>
I'd be very happy about some help! :)
In support of my comment above if you send a custom header in the ajax function you can process that server side. I think I got the syntax right for accessing the filename from the files
collection
/* ajax: add custom header */
xhr.open('post', 'process.php', true);
xhr.setRequestHeader( 'filename', files[i].name );
xhr.send(canvas.toDataURL('image/jpeg'));
/* php: resort to original md5 name if header failed */
$filename=!empty( $_SERVER['HTTP_FILENAME'] ) ? $_SERVER['HTTP_FILENAME'] : md5(mt_rand()).".jpg";
As I originally forgot to add HTTP_
to the beginning of the custom header ( php ) it initially would not have worked - a simple oversight on my behalf. To correct that I put together a quick demo of using the custom headers idea from end to end, though the code that follows does not entirely emulate your original code with the image processing, canvas, FileReader etc it does show the important aspect of assigning the custom request header and how to process that server-side in php so I hope it will give you an idea how you can implement the original filename feature.
<?php
/*
emulate server side processing of uploaded file -
here it simply sends back the custom headers and
a single POST variable but this would be processing
the image data and saving the file
*/
if( $_SERVER['REQUEST_METHOD']=='POST' ){
ob_clean();
$filename = !empty( $_SERVER['HTTP_FILENAME'] ) ? $_SERVER['HTTP_FILENAME'] : md5( mt_rand() ).".jpg";
$filetype = !empty( $_SERVER['HTTP_FILETYPE'] ) ? $_SERVER['HTTP_FILETYPE'] : 'M.I.A';
$filesize = !empty( $_SERVER['HTTP_FILESIZE'] ) ? $_SERVER['HTTP_FILESIZE'] : 'M.I.A';
$action = !empty( $_POST['action'] ) ? $_POST['action'] : 'M.I.A';
/* send proper content type response header */
header( 'Content-Type: application/json' );
/* add some custom response headers to show how you can pass headers and process them */
header( sprintf( 'Uploaded-Filename: %s', $filename ) );
header( sprintf( 'Uploaded-Filesize: %s', $filesize ) );
header( sprintf( 'Uploaded-Filetype: %s', $filetype ) );
/* send payload back to ajax callback */
exit( json_encode( array(
'filename' => $filename,
'filesize' => $filesize,
'filetype' => $filetype,
'action' => $action
)));
}
?>
<!doctype html>
<html>
<head>
<title>ajax custom headers</title>
<style>
body,body *{
font-family:calibri,verdana,arial;
font-size:0.9rem;
}
</style>
<script>
function bindEvents(){
/* DOM elements */
var oDiv=document.getElementById('results');
var oPre=document.getElementById('headers');
var oBttn=document.getElementById('bttn');
var oFile=document.querySelector('form input[type="file"]');
/* basic callback function to show response */
var callback=function(r,h){
oDiv.innerHTML=r;
oPre.innerHTML=h;
}
oBttn.onclick=function(){
/* as there is only a single file we know the index is zero */
var oCol=oFile.files;
var file=oCol.item(0).name;
var size=oCol.item(0).size;
var type=oCol.item(0).type;
/* ultra basic ajax request with custom request headers */
var xhr=new XMLHttpRequest();
xhr.onreadystatechange=function(){
if( this.readyState==4 && this.status==200 ){
/*
The callback can take whatever arguments we want - here simply
the response and some headers - could easily process specific
response headers rather than all
*/
callback.call( this, this.response, this.getAllResponseHeaders() );
}
};
xhr.open( 'POST', location.href, true );
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
/* add custom request headers - original file details */
xhr.setRequestHeader( 'filename', file );
xhr.setRequestHeader( 'filetype', type );
xhr.setRequestHeader( 'filesize', size );
xhr.send( 'action=headers-test' );
}
}
document.addEventListener( 'DOMContentLoaded', bindEvents, false );
</script>
</head>
<body>
<div id='results'></div>
<pre id='headers'></pre>
<form method='post'>
<input type='file' />
<input type='button' id='bttn' value='Send Ajax Request with custom headers' />
</form>
</body>
</html>
Following on from your comment regarding multiple files all sharing the same name hopefully the following might help.
<form method='post'>
<input type='file' multiple=true />
<input type='button' id='bttn' value='Send Ajax Request with custom headers' />
<div class='photos'></div>
</form>
<script>
document.querySelector( 'form input[type="file"]' ).addEventListener( 'change', function( event ){
// Read files
var files = event.target.files;
// Iterate through files
for( var i = 0; i < files.length; i++ ) {
// Ensure it's an image
if ( files[i].type.match( /image.*/ ) ) {
// Load image
var reader = new FileReader();
/*
assign custom properties to the reader
object which will allow you to access
them within callbacks
*/
reader.filename=files[i].name;
reader.filesize=files[i].size;
reader.filetype=files[i].type;
reader.onload = function( readerEvent ) {
/*
assign each new image with the properties from the reader
- these will be available within the ajax function so you
can set the custom headers
*/
var image = new Image();
image.filename=this.filename;
image.filesize=this.filesize;
image.filetype=this.filetype;
image.onload = function( imageEvent ) {
console.log('image onload - - - > > > > > %s -> %s',this.filename,this.filesize);
// Add element to page
var imageElement = document.createElement('div');
imageElement.classList.add('uploading');
imageElement.innerHTML = '<span class="progress"><span></span></span>';
var progressElement = imageElement.querySelector('span.progress span');
progressElement.style.width = 0;
document.querySelector('form div.photos').appendChild( imageElement );
// Resize image
var canvas = document.createElement('canvas'),
max_size = 1200,
width = image.width,
height = image.height;
if ( width > height ) {
if( width > max_size ) {
height *= max_size / width;
width = max_size;
}
} else {
if( height > max_size ) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage( image, 0, 0, width, height );
// Upload image
var xhr = new XMLHttpRequest();
if( xhr.upload ) {
xhr.upload.addEventListener('progress', function(event) {
var percent = parseInt( event.loaded / event.total * 100 );
progressElement.style.width = percent+'%';
}, false);
xhr.onreadystatechange = function(event) {
if( xhr.readyState == 4 ) {
if( xhr.status == 200 ) {
imageElement.classList.remove('uploading');
imageElement.classList.add('uploaded');
imageElement.style.backgroundImage = 'url('+xhr.responseText+')';
} else {
imageElement.parentNode.removeChild( imageElement );
}
}
}
xhr.open( 'post', location.href, true ); //'process.php'
xhr.setRequestHeader( 'filename', image.filename );
xhr.setRequestHeader( 'filetype', image.filetype );
xhr.setRequestHeader( 'filesize', image.filesize );
xhr.send( canvas.toDataURL('image/jpeg') );
}
};
image.src = readerEvent.target.result;
};
reader.readAsDataURL( files[i] );
}
}
// Clear files
event.target.value = '';
});
</script>