how to get a zip with $.ajax from php server - javascript

I'm trying to send a zip generated by a php server when requested by jquery's $.ajax.
This is my code:
php:
$file = tempnam($a_folder_path, "zip");
$zip = new ZipArchive();
$zip->open($file, ZipArchive::OVERWRITE);
$zip->addFile($path_to_json, 'data.json');
$zip->close();
rename($file, $file . '.zip');
$filename = basename($file . '.zip');
$filepath = $file . '.zip';
while (ob_get_level()) {
ob_end_clean();
}
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filepath));
ob_end_flush();
echo file_get_contents($filepath);
//#readfile($filepath);
javascript:
$.ajax(
{
url: myUrl,
type: 'POST',
data: {
"leData" : "some_data"
},
context: document.body,
cache: false,
success: function(data) {
console.log(data);
console.log(data.length);
var bytes = new Uint8Array(data.length);
for (var i=0; i<data.length; i++) {
bytes[i] = data.charCodeAt(i);
}
blob = new Blob([bytes], {type: "application/zip"})
saveAs(blob, "test.zip");//trying to access data with FileSave.js
zip.load(data);//trying to access data with JSZip.js
jsonData = JSON.parse(zip.file('shouldBeThere.json').asText());
},
error: function() {
alert('error');
}
}
);
What happens:
The server creates the zip file I ask it to, and this file isn't corrupted. It contains, among others, shouldBeThere.json.
The server sends data to the frontend.
The console.log(data); line in javascript prints a string almost identical from what I get by opening the zip file created on the server with a text editor.
The console.log(data.length); line in javascript prints a number smaller than the content length of the response header according to chrome's devtools. Maybe a hint to a data corruption.
saveAs creates a zip containing the file it's meant to, with the right name, but when I try to unzip it 7zip shows an error: "an attempt was made to move the file pointer before the beginning of the file".
6.JSZip seems to load the data but then zip.file('shouldBeThere.json') is null.
The problem is I have no idea if the problem comes from php or from javascript. I don't know if php is sending a corrupted zip or if javascript isn't reading it right.
I have tried all the php headers combinations and methods i've found on the internet. I have also tried to do things differently in javascript: using an ArrayBuffer instead of Uint8Array, passing bytes instead of data to zip.load(), using {type: "application/octet-stream"} in Blob().

I finally found a solution: It has to be specified to ajax the received data type, and then convert this unicode data into characters. Here is my new javascript code:
$.ajax(
{
url: myUrl,
type: 'POST',
data: {
"leData" : "some_data"
},
context: document.body,
cache: false,
dataType: 'text', //solution code
mimeType: 'text/plain; charset=x-user-defined', //solution code
success: function(data) {
console.log(data);
console.log(data.length);
newContent = ""; //solution code
for (var i = 0; i < data.length; i++) { //solution code
newContent += String.fromCharCode(data.charCodeAt(i) & 0xFF); //solution code
}
var bytes = new Uint8Array(newContent.length); //modified
for (var i=0; i<newContent.length; i++) { //modified
bytes[i] = newContent.charCodeAt(i); //modified
}
blob = new Blob([bytes], {type: "application/zip"})
saveAs(blob, "test.zip");
zip.load(newContent); //modified
jsonData = JSON.parse(zip.file('shouldBeThere.json').asText());
},
error: function() {
alert('error');
}
}
);
My php code was fine, it even worked without headers. Here is the minimal php code I need:
$file = tempnam($a_folder_path, "zip");
$zip = new ZipArchive();
$zip->open($file, ZipArchive::OVERWRITE);
$zip->addFile($path_to_json, 'data.json');
$zip->close();
rename($file, $file . '.zip');
echo file_get_contents($file . '.zip');
Solution inspired by this

I was searching for a solution for following problem:
"Sending a request on php url for creating a zip of a directory and downloading it by using ajax response."
Following code works:
PHP part for zip:
// Function for creating a zip of a directory
function zipFilesAndDownload($directory, $file_names) {
$zip = new ZipArchive();
if ($zip->open("../temp/$directory.zip", ZIPARCHIVE::CREATE) !== TRUE) {
exit("Error on creating '../temp/$directory.zip'");
}
foreach ($file_names as $file) {
$zip->addFile($file, substr($file, strrpos($file, "/") + 1));
}
$zip->close();
readfile("../temp/$directory.zip");
unlink("../temp/$directory.zip");
}
// Code for creating array of filenames
$directory = $_POST['directory']; // e.g. a directory with ID "123"
$dirToZip = "../uploaddir/$directory";
if ($handle = opendir($dirToZip)) {
$file_names = array();
while (($file = readdir($handle)) !== false) {
if ($file != "." && $file != "..") {
array_push($file_names, "$dirToZip/$file");
}
}
closedir($handle);
zipFilesAndDownload($directory, $file_names);
}
JS part:
$(document).on('click', '#downloadDirectory', function () {
var directory = '123';
$.ajax({
type: 'POST',
url: '../createZip.php',
data: {"directory": directory},
dataType: 'text',
mimeType: 'text/plain; charset=x-user-defined',
success: function (data) {
var bytes = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
bytes[i] = data.charCodeAt(i);
}
blob = new Blob([bytes], {type: "application/zip"})
saveAs(blob, "pictures.zip"); // "saveAs" function is provided in FileSaver.js
}
});
});

I update your JS code for JSZip 3+ version.
It's more easy. I hope i'll be useful to someone.
$.ajax(
{
url : url,
type: "POST",
data: $('#form').serialize(),
cache: false,
dataType: 'text',
responseType :'blob',
mimeType: 'text/plain; charset=x-user-defined',
success: function(data) {
JSZip.loadAsync(data)
.then(function (zip){
return zip.generateAsync({type: "blob"})})
.then(function (blob){
var date = new Date();
saveAs(blob, date.getFullYear() + "" + (date.getMonth() + 1) + "" + date.getDate() + "" + date.getHours() + "" + date.getMinutes() + "" + date.getSeconds()+".zip");
});
},
error: function() {
alert('error');
}
);

Related

How do I upload a file through Ajax and php without form?

I am trying to upload a file to the same place in the server, The file is a html file the is created through javascript. I currently have it set up so I am able to create and download the file (Contents is equal to some text) This part works. I am trying to make it so that on button press instead of the page providing me a link to download the file, the file gets upload to a folder on the webserver.
The text file is made with the following code:
makeTextFile = function (text) {
var data = new Blob([text], {type: 'text/plain'});
if (textFile !== null) {
window.URL.revokeObjectURL(textFile);
}
textFile = window.URL.createObjectURL(data);
return textFile;
};
link.href = makeTextFile(contents);
This is the Ajax code:
var form_data = new FormData();
form_data.append("file", textFile);
$.ajax({
url:"upload.php",
method:"POST",
data: form_data,
contentType: false,
cache: false,
processData: false,
beforeSend:function(){
console.log("File Uploading");
},
success:function(data)
{
console.log("File Uploaded: " + data);
}
});
upload.php
if($_FILES["file"]["name"] != '')
{
$test = explode('.', $_FILES["file"]["name"]);
$ext = end($test);
$name = rand(100, 999) . '.' . $ext;
$location = '/uploads/' . $name;
move_uploaded_file($_FILES["file"]["tmp_name"], $location);
echo '<img src="'.$location.'" height="150" width="225" class="img-thumbnail" />';
}
?>
Thanks :)

JSPDF .output Image + txt

So i have a jspdf script im working with.
.save works as expected and outputs the image + text elements.
How ever
doc.output() = only text
doc.output('datauristring') = corrupted pdf
I think im missing something
here is an example of my code
var imgData = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQIAdgB2AAD/examplestringonly...';
var doc = new jsPDF('p', 'pt');
doc.text(35, 340, "data notes");
doc.addImage(imgData, 'JPEG', 350, 40, 200, 40);
doc.output('datauristring');
Solved hope this helps anyone.
.output() was correctly sending all the date, how ever the base64 was being corrupted. so i made the php handle the datauri, and used base64_decode() on it.
then made it save to file.
fixes the issues completely.
var pdf = doc.output('datauri');
var data = new FormData();
data.append("data" , pdf);
data.append("id" , id);
$.ajax({
url: 'upload.php',
data: data,
dataType: 'text',
processData: false,
contentType: false,
type: 'POST',
success: function (response) {
console.log('Exit to send request');
},
error: function (jqXHR) {
console.log('Failure to send request');
}
});
<?php
if(!empty($_POST['data'])){
$data = str_replace(' ','+',$_POST['data']);
$data = substr($imgData,strpos($imgData,",")+1);
$data = base64_decode($imgData);
$id = $_POST['id'];
$fname = "test.pdf"; // name the file
$file = fopen("api/warranty/pdf/" .$id, 'w'); // open the file path
fwrite($file, $data); //save data
fclose($file);
} else {
echo "No Data Sent";
}

Uploading image using ajax

I have a variables src and image,
var src = $("#img_tag").attr('src');
var image = new Image();
I want to pass this image variable to a public function in php to upload. What is it that exactly that I need to pass, is it the src, the base64, the image itself as new FormData ?
Right now I have:
var data = new FormData();
var src = $("#img_tag").attr('src');
var image = new Image();
data.append('Image', image);
console.log(data);
$.ajax({
url: "/products/image_upload",
type: "POST",
data: data,
processData: false,
contentType: false,
success: function (msg) {
console.log(msg+"---Image was uploaded");
}
error: function(err) {
console.log(err);
}
});
In my view:
public function image_upload() {
$this->autoRender = false;
echo json_encode($this->data);
if($this->request->is('post'))
{
if(!empty($_FILES['Image']['name'])) {
$file = $_FILES['Image'];
echo json_encode($file);
$ext = substr(strtolower(strrchr($file['name'], '.')), 1);
$arr_ext = array('jpg', 'jpeg', 'gif', 'png');
$temp = explode(".", $file['name']);
$newfilename = $_FILES['Image']['name'];
if(in_array($ext, $arr_ext))
{
if(move_uploaded_file($file['tmp_name'], WWW_ROOT . 'img/product-uploads' . DS . $newfilename))
{
echo json_encode("Image uploaded properly");
return json_encode($_FILES);
}
}
}
}
}
And getting:
{"Image":"[object HTMLImageElement]"}---Image was uploaded
BUT IMAGE IS NOT UPLOADED
If you take a look at the FormData.append doc, you will see that the second argument takes a string or a blob.
So passing an HTMLImageElement isn't going to work, if you cant get the image as a Blob or a File using a FormData object doesn't really help.
Since you're trying to upload the src of #img_tag, this will only really work if it is the base64 encoded image.
In this case use data.append('Image', src); and read the data from $_POST['Image'] then clean it up and decode it.
If the image src is a regular url, use $_POST['Image'] with curl to download the image to your server.
I got a bit of a research, and found this article super helpful. (Thanks to that)
I managed to upload an image file to a directory in my server from an image src by getting the base64 encoded image, passed for the controller to decode and upload. (Also thanks to Musa, DanielO, and Rory McCrossan)
In Controller: Code from this article. (I added a custom filename in a datetime format)
public function additional_image_upload() {
$this->autoRender = false;
$base64 = $this->request->data['base64'];
$product_id = $this->request->data['id'];
$baseFromJavascript = $base64;
$data = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $baseFromJavascript));
$t = microtime(true);
$micro = sprintf("%06d",($t - floor($t)) * 1000000);
$date_tmp = new DateTime( date('Y-m-d H:i:s.'.$micro, $t) );
$date = $date_tmp->format("Y-m-d_his.u");
$filepath = WWW_ROOT . 'img/product-uploads' . DS ."$date.jpg"; // or image.jpg
file_put_contents($filepath,$data);
}
In Script: (I passed the base64 encoded image for the controller to handle)
var src = $(this).attr('src');
$.ajax({
url:"/products/additional_image_upload",
data:{
"id": "<?= $id; ?>",
"base64": src
},
type:"POST",
success: function(msg) {
console.log(msg."---Img uploaded");
},
error: function(error) {
console.log(error);
}
});
And all was working great. Added this to help future readers.

How do I send images in an ajax request? [duplicate]

I want to implement a simple file upload in my intranet-page, with the smallest setup possible.
This is my HTML part:
<input id="sortpicture" type="file" name="sortpic" />
<button id="upload">Upload</button>
and this is my JS jquery script:
$("#upload").on("click", function() {
var file_data = $("#sortpicture").prop("files")[0];
var form_data = new FormData();
form_data.append("file", file_data);
alert(form_data);
$.ajax({
url: "/uploads",
dataType: 'script',
cache: false,
contentType: false,
processData: false,
data: form_data,
type: 'post',
success: function(){
alert("works");
}
});
});
There is a folder named "uploads" in the root directory of the website, with change permissions for "users" and "IIS_users".
When I select a file with the file-form and press the upload button, the first alert returns "[object FormData]". the second alert doesn't get called and the"uploads" folder is empty too!?
Can someone help my finding out whats wrong?
Also the next step should be, to rename the file with a server side generated name. Maybe someone can give me a solution for this, too.
You need a script that runs on the server to move the file to the uploads directory. The jQuery ajax method (running on the client in the browser) sends the form data to the server, then a script running on the server handles the upload.
Your HTML is fine, but update your JS jQuery script to look like this:
(Look for comments after // <-- )
$('#upload').on('click', function() {
var file_data = $('#sortpicture').prop('files')[0];
var form_data = new FormData();
form_data.append('file', file_data);
alert(form_data);
$.ajax({
url: 'upload.php', // <-- point to server-side PHP script
dataType: 'text', // <-- what to expect back from the PHP script, if anything
cache: false,
contentType: false,
processData: false,
data: form_data,
type: 'post',
success: function(php_script_response){
alert(php_script_response); // <-- display response from the PHP script, if any
}
});
});
And now for the server-side script, using PHP in this case.
upload.php: a PHP script that is located and runs on the server, and directs the file to the uploads directory:
<?php
if ( 0 < $_FILES['file']['error'] ) {
echo 'Error: ' . $_FILES['file']['error'] . '<br>';
}
else {
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']);
}
?>
Also, a couple things about the destination directory:
Make sure you have the correct server path, i.e., starting at the PHP script location what is the path to the uploads directory, and
Make sure it's writeable.
And a little bit about the PHP function move_uploaded_file, used in the upload.php script:
move_uploaded_file(
// this is where the file is temporarily stored on the server when uploaded
// do not change this
$_FILES['file']['tmp_name'],
// this is where you want to put the file and what you want to name it
// in this case we are putting in a directory called "uploads"
// and giving it the original filename
'uploads/' . $_FILES['file']['name']
);
$_FILES['file']['name'] is the name of the file as it is uploaded. You don't have to use that. You can give the file any name (server filesystem compatible) you want:
move_uploaded_file(
$_FILES['file']['tmp_name'],
'uploads/my_new_filename.whatever'
);
And finally, be aware of your PHP upload_max_filesize AND post_max_size configuration values, and be sure your test files do not exceed either. Here's some help how you check PHP configuration and how you set max filesize and post settings.
**1. index.php**
<body>
<span id="msg" style="color:red"></span><br/>
<input type="file" id="photo"><br/>
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$(document).on('change','#photo',function(){
var property = document.getElementById('photo').files[0];
var image_name = property.name;
var image_extension = image_name.split('.').pop().toLowerCase();
if(jQuery.inArray(image_extension,['gif','jpg','jpeg','']) == -1){
alert("Invalid image file");
}
var form_data = new FormData();
form_data.append("file",property);
$.ajax({
url:'upload.php',
method:'POST',
data:form_data,
contentType:false,
cache:false,
processData:false,
beforeSend:function(){
$('#msg').html('Loading......');
},
success:function(data){
console.log(data);
$('#msg').html(data);
}
});
});
});
</script>
</body>
**2.upload.php**
<?php
if($_FILES['file']['name'] != ''){
$test = explode('.', $_FILES['file']['name']);
$extension = end($test);
$name = rand(100,999).'.'.$extension;
$location = 'uploads/'.$name;
move_uploaded_file($_FILES['file']['tmp_name'], $location);
echo '<img src="'.$location.'" height="100" width="100" />';
}
Use pure js
async function saveFile()
{
let formData = new FormData();
formData.append("file", sortpicture.files[0]);
await fetch('/uploads', {method: "POST", body: formData});
alert('works');
}
<input id="sortpicture" type="file" name="sortpic" />
<button id="upload" onclick="saveFile()">Upload</button>
<br>Before click upload look on chrome>console>network (in this snipped we will see 404)
The filename is automatically included to request and server can read it, the 'content-type' is automatically set to 'multipart/form-data'. Here is more developed example with error handling and additional json sending
async function saveFile(inp)
{
let user = { name:'john', age:34 };
let formData = new FormData();
let photo = inp.files[0];
formData.append("photo", photo);
formData.append("user", JSON.stringify(user));
try {
let r = await fetch('/upload/image', {method: "POST", body: formData});
console.log('HTTP response code:',r.status);
alert('success');
} catch(e) {
console.log('Huston we have problem...:', e);
}
}
<input type="file" onchange="saveFile(this)" >
<br><br>
Before selecting the file Open chrome console > network tab to see the request details.
<br><br>
<small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small>
var formData = new FormData($("#YOUR_FORM_ID")[0]);
$.ajax({
url: "upload.php",
type: "POST",
data : formData,
processData: false,
contentType: false,
beforeSend: function() {
},
success: function(data){
},
error: function(xhr, ajaxOptions, thrownError) {
console.log(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
and this is the php file to receive the uplaoded files
<?
$data = array();
//check with your logic
if (isset($_FILES)) {
$error = false;
$files = array();
$uploaddir = $target_dir;
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' => 'NO FILES ARE SENT','formData' => $_REQUEST);
}
echo json_encode($data);
?>

No file being uploaded while uploading multiple files using AJAX

I am trying to upload multiple files with only 1 AJAX request. But I am facing some problems and they are:
UPDATE
No file is being uploaded to the server
It seems to me that multiple AJAX requests are being made as AJAX is inside the for loop.
How to sort these problems?
HTML
<!--<form id="fileupload" method="POST" enctype="multipart/form-data">-->
<input type="file" multiple name="uploadfile[]" id="uploadfile" />
<!--</form>-->
JS
$("#uploadfile").change(function(){
//submit the form here
//$('#fileupload').submit();
var files = $("#uploadfile")[0].files;
for (var i = 0; i < files.length; i++){
//alert(files[i].name);
var data = files[i].name;
$.ajax({
type:'POST',
url: 'mupld.php',
data: data
});
}
//var files = $('#uploadfile').prop("files"); //files will be a FileList object.
//alert(files);
//var names = $.map(files, function(val) { return val.name; }); //names is an array of strings (file names)
});
PHP
<?php
if(isset($_FILES['uploadfile'])){
$errors= array();
foreach($_FILES['uploadfile']['tmp_name'] as $key => $tmp_name ){
$file_name = $key.$_FILES['uploadfile']['name'][$key];
$file_size =$_FILES['uploadfile']['size'][$key];
$file_tmp =$_FILES['uploadfile']['tmp_name'][$key];
$file_type=$_FILES['uploadfile']['type'][$key];
if($file_size > 2097152){
$errors[]='File size must be less than 2 MB';
}
//$query="INSERT into upload_data (`USER_ID`,`FILE_NAME`,`FILE_SIZE`,`FILE_TYPE`) VALUES('$user_id','$file_name','$file_size','$file_type'); ";
$desired_dir="storage";
if(empty($errors)==true){
if(is_dir($desired_dir)==false){
mkdir("$desired_dir", 0700); // Create directory if it does not exist
}
if(is_dir("$desired_dir/".$file_name)==false){
move_uploaded_file($file_tmp,"$desired_dir/".$file_name);
}
else{ // rename the file if another one exist
$new_dir="$desired_dir/".$file_name.time();
rename($file_tmp,$new_dir) ;
}
//mysql_query($query);
}
else{
print_r($errors);
}
}
if(empty($error)){
echo "Success";
}
}
?>
js at Question does not appear to upload File object, but File.name at var data = files[i].name; ? <!-- --> is not valid comment in javascript
Try using FormData()
$("#uploadfile").change(function() {
var files = $("#fileupload")[0];
$.ajax({
type:"POST",
url: "mupld.php",
data: new FormData(files),
processData: false,
contentType: false
});
});
Use it
AJAX File Upload plugin
<pre>
$("#uploadfile").change(function(){
$.ajaxFileUpload
(
{
url:'mupld.php',
secureuri:false,
fileElementId:'uploadfile[]',
dataType: 'html',
data:{},
success: function (data, status)
{
},
error: function (data, status, e)
{
}
}
);
});
</pre>

Categories