I am trying to implement a drag-and-drop image upload.
I found a rather simple script online and adapted to my use.
On my local installation the file uploads perfectly fine, but not on the server.
From my debugging attempts the $_SERVER['HTTP_X_FILENAME'] does not even get set by php.
I tried the following:
- Making sure that the upload folder is set to 755
- Changing the php temporary upload path and increasing the maximum allowed file size
No php or js errors of any kind occur.
Since I have the die(print_r($_SERVER)); in the php, I get the $_SERVER dump using the chrome inspector, it does not contain HTTP_X_FILENAME index.
My php is:
<?php
$fn = (isset($_SERVER['HTTP_X_FILENAME']) ? $_SERVER['HTTP_X_FILENAME'] : false);
if ($fn) {
// AJAX call
file_put_contents(
'../usr/photos/' . $fn,
file_get_contents('php://input')
);
echo "$fn uploaded";
exit();
}
else {
// form submit
if(!$_FILES['fileselect']) die(print_r($_SERVER));
else $files = $_FILES['fileselect'];
foreach ($files['error'] as $id => $err) {
if ($err == UPLOAD_ERR_OK) {
$fn = $files['name'][$id];
move_uploaded_file(
$files['tmp_name'][$id],
'../usr/photos/' . $fn
);
echo "<p>File $fn uploaded.</p>";
}
}
}
The js is as follows:
//Drag and drop photo upload
(function() {
// getElementById
function $id(id) {
return document.getElementById(id);
}
// output information
function Output(msg) {
var m = $id("messages");
m.innerHTML = msg + m.innerHTML;
}
// file drag hover
function FileDragHover(e) {
e.stopPropagation();
e.preventDefault();
e.target.className = (e.type == "dragover" ? "hover" : "");
}
// file selection
function FileSelectHandler(e) {
// cancel event and hover styling
FileDragHover(e);
// fetch FileList object
var files = e.target.files || e.dataTransfer.files;
// process all File objects
for (var i = 0, f; f = files[i]; i++) {
ParseFile(f);
UploadFile(f);
}
}
// output file information
function ParseFile(file) {
/*Debug*/
Output(
"<p>File information: <strong>" + file.name +
"</strong> type: <strong>" + file.type +
"</strong> size: <strong>" + file.size +
"</strong> bytes</p>"
);
// display an image
if (file.type.indexOf("image") == 0) {
var reader = new FileReader();
reader.onload = function(e) {
Output(
"<p>" +
//"<strong>" + file.name + ":</strong><br />" +
'<img width="130" height="100" src="' + e.target.result + '" />' +
'<br />' +
'<input type="text" name="photo_name" value="'+ file.name +'" />' +
'<br />' +
'<input type="text" name="photo_caption" value="Caption" /></p>'
);
}
reader.readAsDataURL(file);
}
// display text
if (file.type.indexOf("text") == 0) {
var reader = new FileReader();
reader.onload = function(e) {
Output(
"<p><strong>" + file.name + ":</strong></p><pre>" +
e.target.result.replace(/</g, "<").replace(/>/g, ">") +
"</pre>"
);
}
reader.readAsText(file);
}
}
// upload JPEG files
function UploadFile(file) {
// following line is not necessary: prevents running on SitePoint servers
if (location.host.indexOf("sitepointstatic") >= 0) return
var xhr = new XMLHttpRequest();
if (xhr.upload && (file.type == "image/jpeg" || file.type == "image/png") && file.size <= $id("MAX_FILE_SIZE").value) {
// create progress bar
var o = $id("progress");
var progress = o.appendChild(document.createElement("p"));
progress.appendChild(document.createTextNode("upload " + file.name));
// progress bar
xhr.upload.addEventListener("progress", function(e) {
var pc = parseInt(100 - (e.loaded / e.total * 100));
progress.style.backgroundPosition = pc + "% 0";
}, false);
// file received/failed
xhr.onreadystatechange = function(e) {
if (xhr.readyState == 4) {
progress.className = (xhr.status == 200 ? "success" : "failure");
}
};
// start upload
xhr.open("POST", $id("upload").action, true);
xhr.setRequestHeader("X_FILENAME", file.name);
xhr.send(file);
}
}
// initialize
function Init() {
var fileselect = $id("fileselect"),
filedrag = $id("filedrag"),
submitbutton = $id("submitbutton");
// file select
fileselect.addEventListener("change", FileSelectHandler, false);
// is XHR2 available?
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// file drop
filedrag.addEventListener("dragover", FileDragHover, false);
filedrag.addEventListener("dragleave", FileDragHover, false);
filedrag.addEventListener("drop", FileSelectHandler, false);
filedrag.style.display = "block";
// remove submit button
submitbutton.style.display = "none";
}
}
// call initialization file
if (window.File && window.FileList && window.FileReader) {
Init();
}
})();
Thank you in advance.
You probably will have solved your problem now, but I'm posting this solution here to help others who come here with the same problem. In your js, there is a line that reads
xhr.setRequestHeader("X_FILENAME", file.name);
but should read
xhr.setRequestHeader("X-FILENAME", file.name);
since underscores are deprecated in later Apache releases (see also
Header names with underscores ignored in php 5.5.1 / apache 2.4.6)
I had this problem on one of my Ubuntu WAMP installations. Your upload URL (the POST URL specified on the Javascript side of things) needs to be a fully qualified path not a relative path. I can't see the value however but seems to be the value of whatever $id("upload").action is in your code. You can confirm this is the cause by looking at the apache logs if you have access to them. If you see 404 errors when trying to send a file then this is your problem. Thats assuming the request even hits your server at all.
Related
I'm able to get the file uploaded and in to the directory I want so that part seems to work but I'm not sure why I'm getting a parse error in the js console in chrome. Because of this error my bottom javascript won't execute and I need it to do so.
Here's the ajax:
var files;
// Add events
$('input[type=file]').on('change', prepareUpload);
// Grab the files and set them to our variable
function prepareUpload(event)
{
files = event.target.files;
}
$('form').on('submit', uploadFiles);
// Catch the form submit and upload the files
function uploadFiles(event)
{
event.stopPropagation(); // Stop stuff happening
event.preventDefault(); // Totally stop stuff happening
// START A LOADING SPINNER HERE
// Create a formdata object and add the files
var data = new FormData();
$.each(files, function(key, value)
{
data.append(key, value);
});
$.ajax({
url: 'submit.php?files',
type: 'POST',
data: data,
cache: false,
dataType: 'json',
processData: false, // Don't process the files
contentType: false, // Set content type to false as jQuery will tell the server its a query string request
success: function(data, textStatus, jqXHR)
{
alert(data);
script = $(data).text();
$.globalEval(script);
if(typeof data.error === 'undefined')
{
// Success so call function to process the form
submitForm(event, data);
}
else
{
// Handle errors here
console.log('ERRORS: ' + data.error);
}
},
error: function(jqXHR, textStatus, errorThrown)
{
// Handle errors here
console.log('ERRORS: ' + textStatus);
// STOP LOADING SPINNER
}
});
}
Here's the html:
<?php
echo '<span class="new_profile_save_upload_image_span"><img src="'.$url_root.'/images/615721406-612x612.jpg"/ class="new_profile_save_upload_image_img"></span>';
?>
<form action="" method="post" enctype="multipart/form-data" name="new_profile_save_upload_image_input_form" id="new_profile_save_upload_image_input_form">
<input type="file" id="new_profile_save_upload_image_input" name="new_profile_save_upload_image_input" multiple="" accept="image/x-png,image/gif,image/jpeg"/>
<input type="submit" value="Upload Image" name="submit">
</form>
And here is the php:
<?php
// get mysqli db connection string
$mysqli = new mysqli("localhost", "psych_admin", "asd123", "psych");
if($mysqli->connect_error){
exit('Error db');
}
// Get theme settings and theme colours and assign the theme colour to the
theme name
$stmt = $mysqli->prepare("SELECT name FROM user_profiles WHERE rowid=(SELECT
MAX(rowid) FROM user_profiles);");
$stmt->execute();
$result = $stmt->get_result();
while($row_1 = $result->fetch_assoc())
{
$arr_1[] = $row_1;
}
foreach($arr_1 as $arrs_1)
{
$username = $arrs_1['name'];
}
$data = array();
if(isset($_GET['files']))
{
$error = false;
$files = array();
// Make dir for file uploads to be held
if (!file_exists(''.dirname(__FILE__) . '/content/profiles/'.$username.'/avatar'))
{
mkdir(''.dirname(__FILE__) . '/content/profiles/'.$username.'/avatar', 0777, true);
}
$uploaddir = './content/profiles/'.$username.'/avatar/';
foreach($_FILES as $file)
{
if(move_uploaded_file($file['tmp_name'], $uploaddir .basename($file['name'])))
{
$files[] = $uploaddir .$file['name'];
}
else
{
$error = true;
}
}
$data = ($error) ? array('error' => 'There was an error uploading your files') : array('files' => $files);
}
else
{
$data = array('success' => 'Form was submitted', 'formData' => $_POST);
}
echo json_encode($data);
?>
<script>
var scope1 = '<?php echo $url_root;?>';
var scope2 = '<?php echo $username;?>';
var scope3 = '<?php echo $file['name'];?>';
var new_profile_save_upload_image_span_data = '<img src="' + scope1 + '/content/profiles/' + scope2 + '/avatar/' + scope3 + '" class="new_profile_save_upload_image_img">';
$('.new_profile_save_upload_image_span').empty();
$('.new_profile_save_upload_image_span').append(new_profile_save_upload_image_span_data);
</script>
alert(data) doesn't seem to be popping up, so there's something wrong previous to that execution.
I tried this code with simply 'submit.php' but it doesn't seem to work without the 'files' addition to it.
Also do I have the filename correct? Should the file's filename be $file['name'] in php? I'm trying to get the file name as a string and place it in when the default image is (as an image to be displayed), using an img html tag and inserting it via jquery, as you can see at the bottom under .
The ajax should execute this script at the bottom but it doesn't due to the error.
Also is there a nicer way of writing the bottom jquery scripts that I have written?
Error I'm getting:
ERRORS: Syntax Error: Unexpected Token < in JSON at position 103
Thanks in advance.
If you want to return JSON and HTML at the same time, you could put the HTML into an element of the $data array.
<?php
// get mysqli db connection string
$mysqli = new mysqli("localhost", "psych_admin", "asd123", "psych");
if($mysqli->connect_error){
exit('Error db');
}
// Get theme settings and theme colours and assign the theme colour to the
theme name
$stmt = $mysqli->prepare("SELECT name FROM user_profiles WHERE rowid=(SELECT
MAX(rowid) FROM user_profiles);");
$stmt->execute();
$result = $stmt->get_result();
while($row_1 = $result->fetch_assoc())
{
$arr_1[] = $row_1;
}
foreach($arr_1 as $arrs_1)
{
$username = $arrs_1['name'];
}
$data = array();
if(isset($_GET['files']))
{
$error = false;
$files = array();
// Make dir for file uploads to be held
if (!file_exists(''.dirname(__FILE__) . '/content/profiles/'.$username.'/avatar'))
{
mkdir(''.dirname(__FILE__) . '/content/profiles/'.$username.'/avatar', 0777, true);
}
$uploaddir = './content/profiles/'.$username.'/avatar/';
foreach($_FILES as $file)
{
if(move_uploaded_file($file['tmp_name'], $uploaddir .basename($file['name'])))
{
$files[] = $uploaddir .$file['name'];
}
else
{
$error = true;
}
}
$data = ($error) ? array('error' => 'There was an error uploading your files') : array('files' => $files);
}
else
{
$data = array('success' => 'Form was submitted', 'formData' => $_POST);
$data['html'] = <<<EOS
<script>
var scope1 = '$url_root';
var scope2 = '$username';
var scope3 = '{$file['name']}';
var new_profile_save_upload_image_span_data = '<img src="' + scope1 + '/content/profiles/' + scope2 + '/avatar/' + scope3 + '" class="new_profile_save_upload_image_img">';
\$('.new_profile_save_upload_image_span').empty();
\$('.new_profile_save_upload_image_span').append(new_profile_save_upload_image_span_data);
</script>
EOS;
}
echo json_encode($data);
?>
Then in the JavaScript you do:
script = $(data.html).text();
It's better to use try-catch block in your PHP code, and send status with the response set to true or false. Also, send the $url_root and $username variables within the JSON object.
See this beginner's guide on Image Uploading with PHP and AJAX to learn everything about creating AJAX handler, validating, saving and sending a response back to the client side.
I don't know why but I can't figure out how to upload a picture from the CameraRoll on my AIR app to my server.
Here's my code :
function initCamera(evt:Event):void{
cameraRoll.addEventListener( MediaEvent.SELECT, imageSelected );
cameraRoll.browseForImage();
}
function imageSelected( event:MediaEvent ):void{
//PREVIEW OF THE IMAGE
var mp:MediaPromise = event.data;
mediaPath.text = mp.file.name + "\n" + mp.file.url;
Imgloader.source = mp.file.url;
uploadImage();
function uploadImage():void{
feedbackText.text = "uploadImage";
var req = new URLRequest();
req.url = ( stage.loaderInfo.parameters.f )? stage.loaderInfo.parameters.f : 'http://www.****-**/***/uploadImg.php';
mp.file.upload(req);
mp.file.addEventListener( ProgressEvent.PROGRESS, progress_func );
}
}
Any idea what's wrong with the code ?
The function "upload" is triggered but nothing is uploading... (and progress_func is not triggered)
I am selecting a file and sending it through XMLXttpRequest like this :
var upload_form = $('#upload_form'),
file_input = $('#file_input'),
file_list = $('#file_list'),
submit_btn = $('#submit_btn'),
uploaders = [];
file_input.on('change', onFilesSelected);
upload_form.on('submit', onFormSubmit);
/**
* Loops through the selected files, displays their file name and size
* in the file list, and enables the submit button for uploading.
*/
function onFilesSelected(e) {
var files = e.target.files,
file,
list_item,
uploader;
for (var i = 0; i < files.length; i++) {
file = files[i];
uploader = new ChunkedUploader(file);
uploaders.push(uploader);
list_item = $('<li>' + file.name + '(' + file.size.formatBytes() + ') <button>Pause</button></li>').data('uploader', uploader);
file_list.append(list_item);
}
file_list.show();
submit_btn.attr('disabled', false);
}
so for each file that I add, I create a new ChunkedUploader object, which chunks the file in little 1MB files.
The code for the ChunkedUploader object is as follows:
function ChunkedUploader(file, options) {
if (!this instanceof ChunkedUploader) {
return new ChunkedUploader(file, options);
}
this.file = file;
this.options = $.extend({
url: 'index/upload'
}, options);
this.file_size = this.file.size;
this.chunk_size = (1024 * 100); // 100KB
this.range_start = 0;
this.range_end = this.chunk_size;
if ('mozSlice' in this.file) {
this.slice_method = 'mozSlice';
}
else if ('webkitSlice' in this.file) {
this.slice_method = 'webkitSlice';
}
else {
this.slice_method = 'slice';
}
this.upload_request = new XMLHttpRequest();
this.upload_request.onload = this._onChunkComplete();
}
_upload: function() {
var self = this,
chunk;
// Slight timeout needed here (File read / AJAX readystate conflict?)
setTimeout(function() {
// Prevent range overflow
if (self.range_end > self.file_size) {
self.range_end = self.file_size;
}
chunk = self.file[self.slice_method](self.range_start, self.range_end);
self.upload_request.open('POST', self.options.url, true);
self.upload_request.overrideMimeType('application/octet-stream');
if (self.range_start !== 0) {
self.upload_request.setRequestHeader('Content-Range', 'bytes ' + self.range_start + '-' + self.range_end + '/' + self.file_size);
}
self.upload_request.send(chunk);
}, 200);
},
It all works okay, but on the PHP end I receive nothing through :$_GET,$_POST or $_FILE. I can see in Firebug that raw data is being sent through post, there are some gibberish data being sent, I presume it's the small chunk that I just cropped from the original file. I have looked everywhere and I can't find anything releated to this case.
Can you point out what I am doing wrong, because I have to no clue.
You may want to file_get_contents('php://input') instead: this is the raw request body, whereas $_POST is already a parsed representation.
See http://php.net/manual/en/wrappers.php.php#wrappers.php.input
I'm trying to upload a canvas.todataurl() image (getUserMedia) to server using jQuery post and php to handle the data, but I'm having some problems. All the images I'm uploading end up being corrupted, half of the image is missing. I also have a MySQL database where I'm storing data related to the image (title, text, date and the like). It seems that the more I have the related data the more the image get corrupted.
Therefore, I'm wondering is this a browser limitation or does this have something to do with jQuery post. I've also checked the PHP max_post_size and it's 16mb, so that shouldn't be a problem. I don't have access to the server settings. I'm quite puzzled with this, what can I do? Is it possible to divide the canvas.todataurl() to multiple parts and then post?
JavaScript
window.addEventListener('DOMContentLoaded', function() {
var video = document.getElementById('videoStream');
var canvas = document.getElementById('canvasImage');
var status = document.getElementById('status');
var button = document.getElementById('button');
//var others = document.getElementById('others');
var imageHolder;
document.getElementById('form').style.display = 'none';
var image = null; // kuvan datauri joka lähtee php:lle
window.URL || (window.URL = window.webkitURL || window.mozURL || window.msURL);
navigator.getUserMedia || (navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia);
// toString : function() {return "video,audio";} canarya varten
if (navigator.getUserMedia) {
navigator.getUserMedia({video: true, audio: false, toString : function() {return "video,audio";}}, onSuccess, onError);
} else {
status.innerText = "getUserMedia is not supported in your browser, sorry :(";
}
function onSuccess(stream) {
var source;
if (window.webkitURL) {
source = window.webkitURL.createObjectURL(stream);
} else {
source = stream; // Opera ja Firefox
}
video.width = 500;
video.height = 375;
video.autoplay = true;
video.src = source;
}
function onError() {
status.innerText = "Please allow access to your webcam.";
}
button.addEventListener('mousedown', function() {
// Poistetaan aikaisempi kuva jos sellaista on
//document.body.removeChild(imageHolder);
// luodaan kuva uudestaan
imageHolder = document.createElement('figure');
imageHolder.id = 'imageHolder';
document.body.appendChild(imageHolder);
img = document.createElement('img');
imageHolder.appendChild(img);
// kuva on yhtäsuuri kuin video
canvas.width = video.width;
canvas.height = video.height;
img.width = 350;
img.height = 225;
// piirretään canvasille kuva videosta
var context = canvas.getContext('2d');
context.drawImage(video, 0, 0, canvas.width, canvas.height);
}, false);
button.addEventListener('mouseup', function() {
// Canvasilta kuvaksi levylle tallentamista varten
canvas.style.display = 'none';
video.style.display = 'none';
button.style.display = 'none';
others.style.display = 'none';
document.getElementById('form').style.display = 'block';
image = canvas.toDataURL('image/png');
img.src = image;
}, false);
// jquery post
$('#send').click(function(){
var image2 = image.replace('data:image/png;base64,', '');
$.post('upload.php',
{
title: $('#title').val(),
blog: $('#blog').val(),
category: $('#category').val(),
author: $('#author').val(),
imagename: image2
});
});
}, false);
PHP upload.php
define('UPLOAD_DIR', 'images/');
$img = $_POST['imagename'];
$img = str_replace(' ','+', $img);
$data = base64_decode($img);
$file = UPLOAD_DIR . uniqid() . '.png';
$success = file_put_contents($file, $data);
print $success ? $file : 'Tiedoston tallennus ei sitten onnistu millään...';
$imagename = $file; // this is the file name for the MySQL database
My problem is (I think) image = canvas.toDataURL('image/png'); and the jQuery post.
The canvas.toDataUrl() string is about 700 000 letters long.
You might wanna try this:
<?php
$decoded = "";
for ($i=0; $i < ceil(strlen($encoded)/256); $i++)
$decoded = $decoded . base64_decode(substr($encoded,$i*256,256));
?>
I got it from here: http://www.php.net/manual/en/function.base64-decode.php#92980
The code basically tries to partially decodes the base64 string. I haven't tested this though. I've never dealt with base64 images as big as what you're working with.
Split it, use two, variables and merge in php, works fine. ;-)
var resourcelength_all = resource.length;
var resourcelength_split = resourcelength_all / 2;
var resource_part1 = resource.substr(0, resourcelength_split);
var resource_part2 = resource.substr(resourcelength_split, resourcelength_all);
I use the script below to create images on the fly, and display them via ajax and a div however the PHP image header is not being read and instead of an image been displayed I just get the image source code. Any ideas how force php to read the header as an image?
<?php
header('Content-Type: image/jpeg'); // JPG picture
$root = $_SERVER['DOCUMENT_ROOT'];
require("$root/include/mysqldb.php"); //mysql login details
require("$root/include/mysql_connect.php"); //mysql connect
if(!empty($small)) { $size = "50"; $folder = "thumnail_user_images"; } else { $size = "250"; $folder = "large_user_images"; }
$dice_image = $_GET["image"];
if (!file_exists("$root/$folder/$dice_image"))
{
require("$root/include/image_resizing_function.php"); //create image
$image = new SimpleImage();
$image->load("$root/raw_user_images/$dice_image");
$image->resizeToWidth($size);
$image->save("$root/$folder/$dice_image");
$image->output();
}
else {
readfile("$root/$folder/$dice_image");
}
And this is the Ajax a use to swap out the images:
<script type="text/javascript">
var bustcachevar=1
var loadedobjects=""
var rootdomain="https://"+window.location.hostname
var bustcacheparameter=""
function ajaxpage(url, containerid){
var page_request = false
if (window.XMLHttpRequest)
page_request = new XMLHttpRequest()
else if (window.ActiveXObject){
try {
page_request = new ActiveXObject("Msxml2.XMLHTTP")
}
catch (e){
try{
page_request = new ActiveXObject("Microsoft.XMLHTTP")
}
catch (e){}
}
}
else
return false
page_request.onreadystatechange=function(){
loadpage(page_request, containerid)
}
if (bustcachevar)
bustcacheparameter=(url.indexOf("?")!=-1)? "&"+new Date().getTime() : "?"+new Date().getTime()
page_request.open('GET', url+bustcacheparameter, true)
page_request.send(null)
}
function loadpage(page_request, containerid){
if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1))
document.getElementById(containerid).innerHTML=page_request.responseText
}
function loadobjs(){
if (!document.getElementById)
return
for (i=0; i<arguments.length; i++){
var file=arguments[i]
var fileref=""
if (loadedobjects.indexOf(file)==-1){
if (file.indexOf(".js")!=-1){
fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", file);
}
else if (file.indexOf(".css")!=-1){
fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet");
fileref.setAttribute("type", "text/css");
fileref.setAttribute("href", file);
}
}
if (fileref!=""){
document.getElementsByTagName("head").item(0).appendChild(fileref)
loadedobjects+=file+" "
}
}
}
</script>
Make sure there's no extra whitespace after the closing ?> in the php files referenced by require statements. I had that problem and was scratching my head for days before finding the answer.