I get the feeling I'm missing something terribly obvious with this, so I'm hoping someone can point me in the right direction. This question specifically pertains to the jQuery file upload script found http://blueimp.github.com/jQuery-File-Upload/
I loaded this very nice and handy set of files on my server and it worked great out of the box. I'd like to enable dynamically creating user directories for users to upload files, and I've followed the directions given here: https://github.com/blueimp/jQuery-File-Upload/wiki/PHP-user-directories
I've opened up the server/php/index.php file, which contains these lines:
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
$upload_handler = new UploadHandler();
and I've edited the file to read as follows:
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
require('upload.class.php');
class CustomUploadHandler extends UploadHandler {
protected function get_user_id() {
$userid = 99771;
return $userid;
}
}
$upload_handler = new CustomUploadHandler(array(
'user_dirs' => true
));
Obviously since this isn't live yet, I'm testing with a static user id (tested with and without quotes), but I don't see how this makes a difference. When I try running this, I get the error:
SyntaxError: JSON.parse: unexpected character
What could this be from? I've also tried putting the inherited class in the UploadHandler.php file instead, but I get the same error message.
http://gloryplus.com/index.php?route=product/product&path=82&product_id=172
for ( ; i < len; i++ ) {
file = this.files[i];
if (!!file.type.match(/image.*/)) {
if ( window.FileReader ) {
reader = new FileReader();
reader.onloadend = function (e) {
showUploadedItem(e.target.result, file.fileName);
};
reader.readAsDataURL(file);
}
if (formdata) {
formdata.append("images[]", file);
}
}
}
if (formdata) {
$.ajax({
url: "upload.php",
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function (res) {
document.getElementById("response").innerHTML = res;
}
});
}
Related
Consider a site which allows user to store files (pdf, docx, jpeg, png, gif only). Part of the html:
<ul>
<li>lola.doc</li>
<li>lola.pdf</li>
<li>lola.jpeg</li>
<li>lola.docx</li>
</ul>
When a user clicks on any of the above, the file either opens or a save dialpg appears. This is fine.
Now I want user to be able to select some of these files (which are on the server). The files will be zipped and echo back to user with a prompt to save. I cannot use above, so I have this option:
html:
<select class="multiple_select " multiple>
<option value="../folder/lola.doc">lola.doc</option>
<option value="../folder/lola.pdf">lola.pdf</option>
<option value="../folder/lola.jpeg">lola.jpeg</option>
<option value="../folder/lola.docx">lola.docx</option>
</select>
<button id="btn" type="button">Download</button>
js:
js:
$('#btn').on('click', function() {
var options_selected = $('select').find('option:selected');
options_selected_le = options_selected.length;
var i;
var options_selected_arr = [];
var options_names_arr = [];
for (i=0; i<options_selected_le; i++) {
options_selected_arr.push(options_selected.eq(i).val());
options_names_arr.push(options_selected.eq(i).text());
}
var fd = new FormData();
fd.append('zipname', zipname);
fd.append('options_selected_arr', JSON.stringify(options_selected_arr));
fd.append('options_names_arr', JSON.stringify(options_names_arr));
$.ajax({
url: 'download_multiple_files.php',
type: 'post',
data: fd,
cache: false,
contentType: false,
processData: false,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-Download", "yes");
},
success: function(response){
alert(response); //I am sure this is wrong
// Do I need js to handle zip file here. I guess php should automatically do this
}
});
});
<?php
session_start();
require 'server_conn.php'; // for connection and holds test_input function
// do some security checks ...
$zipname = 'file.zip';
$arr = json_decode($_POST['options_selected_arr']);
$file_arr = [];
foreach ($arr as $obj) {
array_push($files_arr, test_input($obj));
}
$arr = json_decode($_POST['options_names_arr']);
$files_names_arr = [];
foreach ($arr as $obj) {
array_push($files_names_arr, test_input($obj));
}
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
for ($i=0; $i<$c; $i++) {
$zip->addFile($file_arr[$i], $files_names_arr[$i]);
}
$zip->close();
header('Content-Type: application/zip');
header('Content-Length: ' . filesize($zipname));
header('Content-Disposition: attachment; filename="file.zip"');
readfile($zipname);
unlink($zipname);
?>
Response from server is giberish and there is no error indication. I suspect my php is defective.
I have solved this using 2 methods:
Method 1:
JSZip without php (Each select option already contains file path as value)
The advantage of this method: It does not store the new zip file on the server, so storage is not a problem.
I believe using blob will also allow ziping large files, max size I don't know.
To use this method, one needs to download Filesaver, jszip and jszip utility and add following lines to the html doc body
<script src="../js/lib/jszip.min.js"></script>
<script src="../js/lib/jszip-utils.min.js"></script>
<script src="../js/lib/FileSaver.js"></script>
The js script makes use of Promisejs, which I haven't studied before (but will now do). Below is the js:
$('#btn').on('click', function() {
function urlToPromise(url) {
return new Promise(function(resolve, reject) {
JSZipUtils.getBinaryContent(url, function (err, data) {
if(err) {
reject(err);
} else {
resolve(data);
}
});
});
}
var options_selected = $('select').find('option:selected');
options_selected_le = options_selected.length;
var zipname = 'file.zip';
var Promise = window.Promise;
if (!Promise) {
Promise = JSZip.external.Promise;
}
var i;
var zip = new JSZip();
for (i=0; i<options_selected_le; i++) {
var url = options_selected.eq(i).val();
var filename = options_selected.eq(i).text();
zip.file(filename, urlToPromise(url), {binary:true});
}
zip.generateAsync({type:"blob"}).then(function callback(blob) {
//see FileSaver.js
saveAs(blob, zipname);
//alert('success');
}, function (e) {
alert('Error zipping file(s). Retry');
});
});
Method 2:
Using js and PHP:
First create a folder on the server to hold the zip file, I name the folder 'archive'
This is why I may not vote for this method.
New js:
$('#btn').on('click', function() {
var options_selected = $('select').find('option:selected');
options_selected_le = options_selected.length;
var zipname = 'file.zip';
var fd = new FormData();
fd.append('zipname', zipname);
fd.append('options_selected_arr', JSON.stringify(options_selected_arr));
fd.append('options_names_arr', JSON.stringify(options_names_arr));
$.ajax ({
url: 'download_multiple_files.php',
type: 'post',
data: fd,
cache: false,
contentType: false,
processData: false,
success: function(response){
window.location = response;
}
});
});
New php:
<?php
session_start();
// connect to server, scan input data and do some security checks ...
$zipname = 'file.zip';
$arr = json_decode($_POST['options_selected_arr']);
$file_arr = [];
foreach ($arr as $obj) {
array_push($files_arr, test_input($obj));
}
$arr = json_decode($_POST['options_names_arr']);
$files_names_arr = [];
foreach ($arr as $obj) {
array_push($files_names_arr, test_input($obj));
}
$zip = new ZipArchive();
$path = '/archive/'.$zipname;
if ($zip->open($path, ZipArchive::CREATE)!==TRUE) {
echo 'Cannot zip files'; die;
}
$c = count($file_arr);
for ($i=0; $i<$c; $i++) {
$zip->addFile($file_arr[$i], $files_names_arr[$i]);
}
$zip->close();
echo $path;
mysqli_close($conn);
?>
This will force save dialog to appear. Two pending challenges I have for this method are:
Prevent a new window to open
The save dialog appears with download as file name but without extension .zip. So user should type .zip along with the name. I would prefer the computed zip filename to appear in the save dialog
I am trying to use the Concrete Core Classes to create a user outside of the main folder structure.
For example I had a main folder called
Project One
-- concrete
-- application
-- packages
... etc etc
and another folder called user-upload. In here I have an import-users.php script.
I have a single page which has a form with a file upload element. This takes a CSV and tries to send it to the import-users.php script ready to loop through and create a new user for each row in the CSV. But I keep getting the following error when trying to use the classes:
Fatal error: Class 'Core' not found in path/user_upload/import-users.php on line 6 Call Stack: 0.2009 254592 1. {main}() path/user_upload/import-users.php:0
How can I use the class outside of the concrete5 installation?? Examples would be extremely helpful
Edit 1
Script to upload the CSV
$('#user_upload_submit').click(function () {
var fileInput = document.getElementById('usersfile');
var file = fileInput.files[0];
var formData = new FormData();
formData.append('file', file);
$.ajax({
type: "POST",
url: new_path+"user_upload/import-users.php",
data: formData,
contentType: false,
processData: false,
success: function (msg) {
$('#user_result').html(msg);
},
error: function (jqXHR, exception) {
var msg = '';
if (jqXHR.status === 0) {
msg = 'Not connect.\n Verify Network.';
} else if (jqXHR.status == 404) {
msg = 'Requested page not found. [404]';
} else if (jqXHR.status == 500) {
msg = 'Internal Server Error [500].';
} else if (exception === 'parsererror') {
msg = 'Requested JSON parse failed.';
} else if (exception === 'timeout') {
msg = 'Time out error.';
} else if (exception === 'abort') {
msg = 'Ajax request aborted.';
} else {
msg = 'Uncaught Error.\n' + jqXHR.responseText;
}
alert(msg);
}
});
});
First of all, you should add a validation token to every request you send to the server, and the server-side script should validate the received token.
Then, you should handle the submit in the single page controller.
Let's assume your single page is available at the /test path.
The View of your single page (where you put the HTML and JavaScript) must be saved as /application/single_pages/test.php.
The controller of the single page (where you put the PHP code that handles the requests) must be saved as /application/controllers/single_page/test.php.
In the /application/single_pages/test.php you have to add a validation token to the data to be sent, and you have to call the URL of a controller method (let's call it handleSubmit).
This can be done with this code:
<script>
<?php
$token = Core::make('token');
?>
$('#user_upload_submit').click(function () {
// ...
var formData = new FormData();
formData.append(<?= json_encode($token::DEFAULT_TOKEN_NAME) ?>, <?= json_encode($token->generate()) ?>);
formData.append('file', file);
$.ajax({
url: <?= json_encode($view->action('handleSubmit')) ?>,
data: formData,
// ...
});
});
</script>
Then, your controller file (/application/controllers/single_page/test.php) can be something like this:
<?php
namespace Application\Controller\SinglePage;
use Concrete\Core\Error\UserMessageException;
use Concrete\Core\Http\ResponseFactoryInterface;
use Concrete\Core\Page\Controller\PageController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class Test extends PageController
{
public function handleSubmit()
{
$token = $this->app->make('token');
if (!$token->validate()) {
throw new UserMessageException($token->getErrorMessage());
}
$file = $this->request->files->get('file');
if (!($file instanceof UploadedFile)) {
throw new UserMessageException(t('File not received.'));
}
if (!$file->isValid()) {
throw new UserMessageException($file->getErrorMessage());
}
// Process the file. It's path is $file->getPathname();
// ...
// Send the final response
return $this->app->make(ResponseFactoryInterface::class)->json(true);
}
}
The namespace of the controller and its class name must reflect the URL of the single page.
Examples:
Your single page is available as /test
Full path to the view: /application/single_pages/test.php
Full path to the controller: /application/controllers/single_page/test.php
Namespace of the controller: Application\Controller\SinglePage
Class name of the controller: Test
Your single page is available as /foo/bar/baz
Full path to the view: /application/single_pages/foo/bar/baz.php
Full path to the controller: /application/controllers/single_page/foo/bar/baz.php
Namespace of the controller: Application\Controller\SinglePage\Foo\Bar
Class name of the controller: Baz
I'm having a problem with sending a file from an app I'm developing with phonegap.
I'm new to phonegap, so I might be trying to solve this in an entirely wrong way, so let me describe the the end goal first.
I'm developing a car rental app, I need to make a contact form, so users can leave an order to rent a car.
The form requires user to put in some basic information, like name and phone number, and also attach a photo or a scan of driver's license.
I was able to figure out the basic info part. I'm using $.ajax dataType: 'jsonp', to send the data to the server and then simply e-mail it to my client's address.
But I can find a way to send the file to the server.
I'm using an input[type=file] field to let the user choose what file to upload.
I've tried uploading file using FileTransfer, but apparently input[type=file] gives you some fake file path, that can't be directly used by FileTransfer.upload()
Problem is, I can't understand how can I get a proper file path for FileTransfer.upload function.
I've tried doing it another way, by reading the file using FileReader.
I tried reading the file and setting an image src to the result, but it doesn't work (it show broken image icon instead of an image, the same code works on PC).
I also tried to output it as text, that does output some data (so why doesn't it work for image src?).
Because I did manage to output the data read from the file as text I thought I will send that to the server and save it.
So here is how the code would look like:
On input change I read the file into a global variable
$(".file1").change(function(e){
var caster = e.target;
var files = caster.files;
if(FileReader && files && files.length) {
var fr = new FileReader();
fr.onloadend = function(e) {
//$(".image").attr("src",e.target.result);
window.file1base64 = e.target.result;
}
fr.readAsDataURL(files[0]);
}
});
Then, when user presses a button, I run FileTransfer.upload and then check every 0.1 seconds, whether the file upload is complete
function uploadSuccess(r) {
$(".output").append(" Success ");
window.fileStatus = true;
}
function uploadError(error) {
$(".output").append(" Error "+error.code+" ");
window.fileStatus = true;
window.fileError = error.code;
}
function uploadFile() {
$(".output").append(" uploadFile ");
file = $('.file1').val().split('\\').pop();
$(".output").append(" File-"+file+" ");
if(file){
$(".output").append(" fileExists ");
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = file;
options.mimeType = "image/jpeg";
options.chunkedMode = false;
options.headers = {
Connection: "close"
};
$(".output").append(" FileUploadOptions ");
window.fileStatus = false;
window.fileError = '';
//fileuri = $(".image").attr("src");
fileuri = window.file1base64;
$(".output").append(" fileuri ");
var ft = new FileTransfer();
ft.upload(fileuri, encodeURI("http://***.***/savefile.php"), uploadSuccess, uploadError, options);
$(".output").append(" upload ");
checkFile();
}
}
function checkFile() {
if(!window.fileStatus) {
$(".output").append(" check ");
setTimeout(checkFile, 100);
return;
}
}
After some checks, it prints out Error 3 and I can't figure out what that means or how to fix it.
Server side code is simply this:
Get the file and save it
$dir_name = dirname(__FILE__)."/uploadedimages/";
move_uploaded_file($_FILES["file"]["tmp_name"], $dir_name."test.txt");
But no file is created on the server.
use the FormData object to get the form data (including input file) and submit it this way:
var data = new FormData($('#yourFormID')[0]);
$.ajax({
url: serverURL,
data: data,
cache:false,
contentType: false,
processData: false,
type: 'POST',
error: function(jqXHR,textStatus,errorThrown){
},
success: function(data){
}
});
You should set the FILEURL in some variable and image in some html image element and then use it to transfer the image.
like this:
function onPgCameraSuccess(imageData) {
fileEntry.file(
function(fileObj) {
var previewImage= document.getElementById('SomeImageElement');
fileName=imageData.substr(imageData.lastIndexOf('/')+1);
fileURL=imageData;
previewImage.src =imageData;
$('#SomeTextBox').val(fileName);
});
}
function SubmitPhoto(){
var uOptions = new FileUploadOptions();
var ft = new FileTransfer();
uOptions .fileKey = "keyofyourfileonserver";
uOptions .fileName = fileName;
uOptions .mimeType = "image/jpeg";
uOptions .httpMethod = "POST";
uOptions .params = params;
ft.upload(fileURL,
urlofsvc,
photoSuccess,
photoFail,
uOptions,
true
);}
I have been messing around with Blueimp's Jquery File Upload for a couple weeks on PHP and I am encountering some issues. I am teaching myself how to program in PHP so I am still getting acclimated to how things work, so apologies for any confusion and I will attempt to clarify as best I can.
I need user uploads to be inserted into their specific username folders in the upload destination directory. I have followed the numerous writeups, answers and faqs for turning on said user directories (for this plugin) and at best, a folder is created for the session with a string of misc. characters, but all uploads regardless of the username go to the same folder, not the users' respective folder. Is it possible to tell the script to read and use the specific username/id/etc as a variable? I'm pretty sure it is, but currently I cannot find a clear-cut method. I have tried to keep my code as basic and similar to the released Blueimp versions for the sake of clarity.
So, here goes.
**Working:**User registers, info is inserted into database, a respective username folder is automatically created inside the upload directory and waits patiently to be filled with delicious files.
**Not working:**Upload script is not reading the username/variable to be used as the target folder name. When executed, "Error SyntaxError: Unexpected token <" is returned, and inserts the uploaded file into the parent "/uploads/" folder.
index.php (inside the original server/php/ directory)
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
$upload_handler = new UploadHandler();
UploadHandler.php
function __construct($options = null, $initialize = true, $error_messages = null) {
$this->response = array();
$this->options = array(
'script_url' => $this->get_full_url().'/'.basename($this->get_server_var('SCRIPT_NAME')),
'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/../../uploads/'.$username.'/',
'upload_url' => $this->get_full_url().'/../../uploads/'.$username.'/',
'user_dirs' => false,
'mkdir_mode' => 0755,
'param_name' => 'files',
My understanding is that the $username variable needs to be defined, but where and how is where I'm lost. I can have the page call and print the username, id, email, and pretty much anything else -on- the page, but the variable/token is not translating into the upload directory script.
Thanks in advance for any input.
You will need to edit your index.php to set the $username variable.
Update the code to this:
require('UploadHandler.php');
$upload_handler = new UploadHandler($username);
In the UploadHandler.php file, update the __construct to this:
function __construct($username, $options = null, $initialize = true, $error_messages = null) {
It should now capture the $username being passed through.
You can try to restore the original server/php/UploadHandler.php then follow this article in the wiki:
jQuery-File-Upload wiki - PHP-user-directories
"To provide your own implementation, you can override the get_user_id method"
edit your index.php:
<?php
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
class CustomUploadHandler extends UploadHandler {
protected function get_user_id() {
return $_REQUEST['username'];
}
protected function set_additional_file_properties($file) {
$file->deleteUrl = $this->options['script_url']
.$this->get_query_separator($this->options['script_url'])
.$this->get_singular_param_name()
.'='.rawurlencode($file->name)
.'&username='.$_REQUEST['username']
;
$file->deleteType = $this->options['delete_type'];
if ($file->deleteType !== 'DELETE') {
$file->deleteUrl .= '&_method=DELETE';
}
if ($this->options['access_control_allow_credentials']) {
$file->deleteWithCredentials = true;
}
}
}
$upload_handler = new CustomUploadHandler(array(
'user_dirs' => true,
));
and main.js
$(function () {
'use strict';
// Initialize the jQuery File Upload widget:
$('#fileupload').fileupload({
// Uncomment the following to send cross-domain cookies:
//xhrFields: {withCredentials: true},
url: 'server/php/index.php?username=' + username,
});
// Enable iframe cross-domain access via redirect option:
$('#fileupload').fileupload(
'option',
'redirect',
window.location.href.replace(
/\/[^\/]*$/,
'/cors/result.html?%s'
)
);
// Load existing files:
$('#fileupload').addClass('fileupload-processing');
$.ajax({
// Uncomment the following to send cross-domain cookies:
//xhrFields: {withCredentials: true},
url: $('#fileupload').fileupload('option', 'url'),
dataType: 'json',
context: $('#fileupload')[0]
}).always(function () {
$(this).removeClass('fileupload-processing');
}).done(function (result) {
$(this).fileupload('option', 'done')
.call(this, $.Event('done'), {result: result});
});
});
}
Hi all: I have another challenge:
Im using Jquery fileuploader for php from blueimp: https://github.com/blueimp/jQuery-File-Upload
I'm modifying the code to implement the uploader in my web.
I can use it correctly, but the documentation is poor about certain adds or mods:
I'm trying to modify the file UploaderHandler.php to create a folder with the username(unique), but I don't know WHERE to put my mkdir() function...
And want to upload the files changing the names to 1.txt,2.pdf,3.doc...etc,etc,etc
Any help?
PD: I'm thinking about 3 solutions:
1) put my mkdir() function in the login.php, and when the user logs in, it check the folder exists and it's empty... and each time he reloads certain .php files. Not the best solution, I guess.
2) put mkdir() function in get_upload_path from uploadHandler.php
3) put my rename() function in get_unique_filename from uploadHandler.php
EDIT: I just tryied the 2) option: I modified the UploadHandler.php.
It works, create the folder with username, and put the uploaded file in the folder. But in the AJAX I don't receive response, and don't create the response line:
UploadHandler.php:
function __construct($options = null, $initialize = true, $error_messages = null){
ob_start();
include('../conexion.php');
include('../session/session.php');
$url_subida = dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$usuario.'/';
if(!is_dir($url_subida)){
mkdir($url_subida, 0777);
}
else{
error_log($url_subida);
}
$this->options = array(
'script_url' => $this->get_full_url().'/',
'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/'.$usuario.'/',
'upload_url' => $this->get_full_url().'/'.$usuario.'/',
[...]
Response in AJAX in the HTML/php:
$(function(){
$('#fileupload').fileupload({
dataType: 'json',
done: function (e, data){
$.each(data.result.files, function (index, file) {
numarch++;
$('<div id="archivo_'+numarch+'" />').appendTo('#files');
if (file.error){
$('<img src="img/x.png" title="Error" alt="Error"/>').appendTo('#archivo_'+numarch);
$('<p class="text-danger"/>').text(file.error).appendTo('#archivo_'+numarch);
}
else{
var newFileDiv = $("<img title='Archivo subido OK' alt='Archivo subido OK'/>
<p>"+file.name+"</p>
<div id='borrarDiv' name='borrarDiv' class='btn btn-danger delete' onclick= borrar_archivo ('archivo_"+numarch+"','"+file.deleteUrl+"','"+file.deleteType+"','"+numarch+"')>
<i class='glyphicon glyphicon-trash'></i>
<span>Borrar</span></div>");
$('#archivo_'+numarch).append(newFileDiv);
}
});
},
progressall: function (e, data){
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress .progress-bar').html(progress + '%');
$('#progress .progress-bar').css('width',progress + '%');
}
});
});
Could you help me to get the response?
Ok, challenge completed:
First: how to upload files to a custom URL, changed by the name of the user:
In the implementation of jQuery Fileupload of BlueImp, you must use 2 files to control the uploads: index.php and uploadhandler.php.
The first creates the object:
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
$upload_handler = new UploadHandler();
You must change it to:
ob_start(); //get session
//include files to operate with database and session
include("../conection.php");
include("../session/session.php");
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
//add var $options. It is an array, you can see it in uploadhandler.php, in the constructor
//you modify the data: the upload_dir and the upload_url
$options = array('upload_dir'=>$usuario.'/', 'upload_url'=>$usuario.'/');
//then add the array to the constructor declaration
$upload_handler = new UploadHandler($options);
With this modification you can send to the constructor the variable upload path.
I hope this help you all.
EDIT: PROBLEM WITH WHITE SPACES
To fight the filename white spaces you must change this function:
protected function get_unique_filename($file_path, $name, $size, $type, $error, $index, $content_range){
while(is_dir($this->get_upload_path($name))){
$name = $this->upcount_name($name);
}
// Keep an existing filename if this is part of a chunked upload:
$uploaded_bytes = $this->fix_integer_overflow(intval($content_range[1]));
while(is_file($this->get_upload_path($name))){
if ($uploaded_bytes === $this->get_file_size($this->get_upload_path($name))){
break;
}
$name = $this->upcount_name($name);
}
//converts the white spaces in _
$name= str_replace(" ", "_", $name);
return $name;
}