Ajax PUT method upload make the uploaded file no longer accessible - php

I tried to make script ajax + php to upload files using put method, it is uploading, but the problem is, it makes the uploaded file no longer accessible.
sample original file :
test
uploaded file :
------WebKitFormBoundaryfHeuzHdIUxsjGOUb
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
test
------WebKitFormBoundaryfHeuzHdIUxsjGOUb--
as you can see if file is video/zip or any other file, it sure will broken.
here is my code
sendFile: function(files, index) {
if ($('a[data-parent="#accordion"]').size() != 0) {
var numRow = $('a[data-parent="#accordion"]').size();
}
else {
var numRow = index;
}
var progress = $('li[data-image="'+files[index].name+'_'+files[index].unique+'"]');
// File size validation
if (files[index].size >= 49999999) { // 49.999.999 = 50Mb max
progress.html('<strong style="color: red;">Cancelled</strong>');
progress.parent().next().html('File Size Exceeded! (MAX:50Mb)');
console.log("File size Exceeded!");
dragNdrop.prepare(files, index+1);
return false;
}
// begin upload
var formData = new FormData();
formData.append('file', files[index]);
var xhr = new XMLHttpRequest();
xhr.open('put', 'ajax/upload', true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", files[index].unique);
xhr.onload = function() {
progress.find('.filestatus').html('<img src="assets/img/wait.gif">').fadeIn(200);
}
xhr.upload.onprogress = function (event) {
// too long to display
}
xhr.send(formData);
how can i upload file using my script without broken it ?
thanks in advance!

Related

Force download a file using PHP through ajax [duplicate]

I want to send an "ajax download request" when I click on a button, so I tried in this way:
javascript:
var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();
download.php:
<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");
readfile("file.txt");
?>
but doesn't work as expected, how can I do ? Thank you in advance
Update April 27, 2015
Up and coming to the HTML5 scene is the download attribute. It's supported in Firefox and Chrome, and soon to come to IE11. Depending on your needs, you could use it instead of an AJAX request (or using window.location) so long as the file you want to download is on the same origin as your site.
You could always make the AJAX request/window.location a fallback by using some JavaScript to test if download is supported and if not, switching it to call window.location.
Original answer
You can't have an AJAX request open the download prompt since you physically have to navigate to the file to prompt for download. Instead, you could use a success function to navigate to download.php. This will open the download prompt but won't change the current page.
$.ajax({
url: 'download.php',
type: 'POST',
success: function() {
window.location = 'download.php';
}
});
Even though this answers the question, it's better to just use window.location and avoid the AJAX request entirely.
To make the browser downloads a file you need to make the request like that:
function downloadFile(urlToSend) {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=fileName;
link.click();
};
req.send();
}
You actually don't need ajax at all for this. If you just set "download.php" as the href on the button, or, if it's not a link use:
window.location = 'download.php';
The browser should recognise the binary download and not load the actual page but just serve the file as a download.
Cross browser solution, tested on Chrome, Firefox, Edge, IE11.
In the DOM, add an hidden link tag:
<a id="target" style="display: none"></a>
Then:
var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)
req.onload = function (event) {
var blob = req.response;
var fileName = null;
var contentType = req.getResponseHeader("content-type");
// IE/EDGE seems not returning some response header
if (req.getResponseHeader("content-disposition")) {
var contentDisposition = req.getResponseHeader("content-disposition");
fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
} else {
fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
}
if (window.navigator.msSaveOrOpenBlob) {
// Internet Explorer
window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
} else {
var el = document.getElementById("target");
el.href = window.URL.createObjectURL(blob);
el.download = fileName;
el.click();
}
};
req.send();
It is possible. You can have the download started from inside an ajax function, for example, just after the .csv file is created.
I have an ajax function that exports a database of contacts to a .csv file, and just after it finishes, it automatically starts the .csv file download. So, after I get the responseText and everything is Ok, I redirect browser like this:
window.location="download.php?filename=export.csv";
My download.php file looks like this:
<?php
$file = $_GET['filename'];
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=".$file."");
header("Content-Transfer-Encoding: binary");
header("Content-Type: binary/octet-stream");
readfile($file);
?>
There is no page refresh whatsoever and the file automatically starts downloading.
NOTE - Tested in the following browsers:
Chrome v37.0.2062.120
Firefox v32.0.1
Opera v12.17
Internet Explorer v11
I prefer location.assign(url);
Complete syntax example:
document.location.assign('https://www.urltodocument.com/document.pdf');
developer.mozilla.org/en-US/docs/Web/API/Location.assign
For those looking a more modern approach, you can use the fetch API. The following example shows how to download a spreadsheet file. It is easily done with the following code.
fetch(url, {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
})
.then(response => response.blob())
.then(response => {
const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.xlsx";
document.body.appendChild(a);
a.click();
})
I believe this approach to be much easier to understand than other XMLHttpRequest solutions. Also, it has a similar syntax to the jQuery approach, without the need to add any additional libraries.
Of course, I would advise checking to which browser you are developing, since this new approach won't work on IE. You can find the full browser compatibility list on the following link.
Important: In this example I am sending a JSON request to a server listening on the given url. This url must be set, on my example I am assuming you know this part. Also, consider the headers needed for your request to work. Since I am sending a JSON, I must add the Content-Type header and set it to application/json; charset=utf-8, as to let the server know the type of request it will receive.
#Joao Marcos solution works for me but I had to modify the code to make it work on IE, below if what the code looks like
downloadFile(url,filename) {
var that = this;
const extension = url.split('/').pop().split('?')[0].split('.').pop();
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "blob";
req.onload = function (event) {
const fileName = `${filename}.${extension}`;
const blob = req.response;
if (window.navigator.msSaveBlob) { // IE
window.navigator.msSaveOrOpenBlob(blob, fileName);
}
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
};
req.send();
},
Decoding a filename from the header is a little bit more complex...
var filename = "default.pdf";
var disposition = req.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1)
{
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1])
filename = matches[1].replace(/['"]/g, '');
}
This solution is not very different from those above, but for me it works very well and i think it's clean.
I suggest to base64 encode the file server side (base64_encode(), if you are using PHP) and send the base64 encoded data to the client
On the client you do this:
let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
let uri = URL.createObjectURL(blob);
let link = document.createElement("a");
link.download = THE_FILE_NAME,
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
This code puts the encoded data in a link and simulates a click on the link, then it removes it.
Your needs are covered by
window.location('download.php');
But I think that you need to pass the file to be downloaded, not always download the same file, and that's why you are using a request, one option is to create a php file as simple as showfile.php and do a request like
var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);
showfile.php
<?php
$file = $_GET["file"]
echo $file;
where file is the file name passed via Get or Post in the request and then catch the response in a function simply
if(ajaxRequest.readyState == 4){
var file = ajaxRequest.responseText;
window.location = 'downfile.php?file=' + file;
}
}
there is another solution to download a web page in ajax. But I am referring to a page that must first be processed and then downloaded.
First you need to separate the page processing from the results download.
1) Only the page calculations are made in the ajax call.
$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },
function(data, status)
{
if (status == "success")
{
/* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
window.location.href = DownloadPage.php+"?ID="+29;
}
}
);
// For example: in the CalculusPage.php
if ( !empty($_POST["calculusFunction"]) )
{
$ID = $_POST["ID"];
$query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
...
}
// For example: in the DownloadPage.php
$ID = $_GET["ID"];
$sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
...
$filename="Export_Data.xls";
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=$filename");
...
I hope this solution can be useful for many, as it was for me.
this works for me
var dataObj = {
somekey:"someValue"
}
$.ajax({
method: "POST",
url: "/someController/someMethod",
data: dataObj,
success: function (response) {
const blob = new Blob([response], { type: 'text/csv' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.csv";
document.body.appendChild(a);
a.click();
}
});

Writing File in Chunks using Ajax and FileReader

I am working on creating a file uploader, where the contents will be written to a remote server in chunks using a 3rd party API. The API provides a WriteFileChunk() method that takes 3 parameters, the target file path, the start position (Int64) and data (string) of bytes.
Each time FileReader receives a chunk of the maximum supported size (16kb), I need to use Ajax to pass this to a PHP file and write it using the API. I suspect that this should be done in the onprogress event of FileReader, however I am at somewhat of a loss given that I cannot find any similar examples.
What would be the best way to implement this using FileReader, ensuring that each chunk is uploaded before writing the next? If onprogress is the best choice, how can I get the current chunk data?
$(document).ready(function()
{
function uploadFile()
{
var files = document.getElementById('file').files;
if (!files.length)
{
alert('Please select a file!');
return;
}
var file = files[0];
var first_byte = 0;
var last_byte = file.size - 1;
// File Reader
var reader = new FileReader();
reader.onerror = function(evt)
{
switch(evt.target.error.code)
{
case evt.target.error.NOT_FOUND_ERR:
alert('File Not Found!');
break;
case evt.target.error.NOT_READABLE_ERR:
alert('File is not readable');
break;
case evt.target.error.ABORT_ERR:
break;
default:
alert('An error occurred reading this file.');
};
};
reader.onprogress = function(evt)
{
if (evt.lengthComputable)
{
var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
console.log(percentLoaded + "%");
if (percentLoaded < 100)
{
$("#upload_progress").progressbar('value', percentLoaded);
}
}
};
reader.onabort = function(evt)
{
alert('File Upload Cancelled');
};
reader.onloadstart = function(evt)
{
$("#upload_progress").progressbar({
value: 0,
max: 100
});
};
reader.onload = function(evt)
{
$("#upload_progress").progressbar('value', 100);
};
reader.onloadend = function(evt)
{
if (evt.target.readyState == FileReader.DONE) // DONE == 2
{
alert("Upload Complete!");
//console.log(evt.target.result);
}
};
var blob = file.slice(first_byte, last_byte + 1);
reader.readAsBinaryString(blob);
}
fileupload_dialog = $( "#dialog-fileupload" ).dialog(
{
autoOpen: false,
height: 175,
width: 350,
modal: true,
buttons:
{
"Upload File": uploadFile
},
close: function()
{
form[ 0 ].reset();
}
});
form = fileupload_dialog.find( "form" ).on( "submit", function( event )
{
event.preventDefault();
uploadFile();
});
$("#file_upload a").click(function()
{
event.preventDefault();
fileupload_dialog.dialog( "open" );
});
});
The main challenge here is that the FileReader will need to read the entire file into memory before it return any usable data to us via the result property, meaning you can't grab chunks from the file while the file is being read (and the progress event won't provide/point to any data):
This property [result] is only valid after the read operation is
complete [...]
Source
As the complete file is loaded into memory there wouldn't really be any benefits chunking the read process (if it was possible), besides from reducing a little lag perhaps between the two processes.
I would suggest the following approach based on the above:
Load the entire file into memory, but as ArrayBuffer
Calculate number of segments required (Math.ceil(fileLength/chunkSize))
Create a chunk using a Uint8Array view for the ArrayBuffer using the offset and chunk length arguments
Send the chunk, wait for response asynchronously, continue to next chunk until left length is <= 0 byte.
The chunk can be converted to a Blob before sending it, if needed:
var chunkBlob = new Blob([chunk], {type: "application/octet-stream"});
Example process
A pseudo server example waiting arbitrarily 100ms between each block of 16kb:
file.onchange = function(e) {
var fr = new FileReader();
fr.onprogress = function(e) {progress.value = e.loaded / e.total};
fr.onload = startUpload.bind(fr);
progress.style.display = "inline-block";
fr.readAsArrayBuffer(e.target.files[0]);
}
// Main upload code
function startUpload() {
// calculate sizes
var chunkSize = 16<<10;
var buffer = this.result;
var fileSize = buffer.byteLength;
var segments = Math.ceil(fileSize / chunkSize);
var count = 0;
progress.value = 0;
// start "upload"
(function upload() {
var segSize = Math.min(chunkSize, fileSize - count * chunkSize);
if (segSize > 0) {
var chunk = new Uint8Array(buffer, count++ * chunkSize, segSize); // get a chunk
progress.value = count / segments;
// send chunk to server (here pseudo cycle for demo purpose)
setTimeout(upload, 100); // when upload OK, call function again for the next block
}
else {
alert("Done");
progress.style.display = "none";
}
})()
}
body {font:16px sans-serif;margin:20px 0 0 20px}
<label>Select any file: <input type=file id="file"></label><br>
<progress id="progress" value=0 max=1 style="display:none" />

jquery ajax file upload with progressbar uploading same file multiple times

i am creating media-bank where user can upload media files and can reuse later
image,audio and videos can be uploaded with the following options
image upload from pc, specify link
audio upload from pc, specify link
video upload from pc, youtube url, facebook embed code
separate forms are created in tabbed layout with class="FormUpload"
upload from pc forms has <input type="file" name="file".../>
while all other forms has <textarea name="file" ...>
my database table looks like
[id, file, type, src,...]
[1, pic.png, image, pc,...]
[2, http://domin/img.png, image, link,...]
$('body').on('submit','.FormUpload',function(e){
e.preventDefault();
var pr = $(this).parents('.tabPanes').find('.progressBar');
var lbl = $(this).parents('.tabPanes').find('.percentLabel');
var url = $(this).attr('action');
var data = new FormData();
if($(this).find('#txtFile[type="file"]').length === 1 ){
data.append('file', $(this).find( '#txtFile' )[0].files[0]);
}else{
data.append('file', $(this).find('#txtFile' ).val());
}
data.append('type',$(this).find('#txtType').val());
data.append('src',$(this).find('#txtSrc').val());
data.append('title',$(this).find('#txtTitle').val());
data.append('tags',$(this).find('#txtTags').val());
if($(this).find('#txtFile[type="file"]').length === 1){//if file is being uploaded from pc
pr.val(100);
fileForm(url,data,pr,lbl);
}else{//else link is provided
linkForm(url,data,pr,lbl);
pr.val(0);
}
return false;
});
function fileForm(url,data,pr,lbl){
`enter code here`$.ajax({
url : url,
type: "POST",
data : data,
contentType: false,
cache: false,
processData:false,
xhr: function(){
//upload Progress
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
pr.val(percent);
}, false);
}
return xhr;
},
mimeType:"multipart/form-data",
}).done(function(res){ //
frm[0].reset();
lbl.html(res);
});
linkForm() also looks like fileForm()
the issue is when I upload image from PC it uploads the same image 3-times some time 5-times in folder as well as database.
I debuged and noticed network tab, ajax request to php file is also being sent multiple times.
tried to replace all jquery code by the following but still same issue but this time frequency looks reduced
$('body').on('submit','.FormUpload',function(e){
e.preventDefault(); //prevent form normal submition
//get progressbar label url_to_hit and form_reference into variables to be used below
var pr = $(this).parents('.tabPanes').find('.progressBar');
var lbl = $(this).parents('.tabPanes').find('.percentLabel');
var url = $(this).attr('action');
var frm = $(this);
//populate formdata
var data = new FormData();
if(frm.find('#txtFile[type="file"]').length === 1 ){
data.append('file', frm.find( '#txtFile' )[0].files[0]);
}else{
data.append('file', frm.find('#txtFile' ).val());
}
data.append('type',frm.find('#txtType').val());
data.append('src',frm.find('#txtSrc').val());
data.append('title',frm.find('#txtTitle').val());
data.append('tags',frm.find('#txtTags').val());
//prepare ajax and callback functions
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener('progress',function(evt){
var percentage = (evt.loaded/evt.total)*100;
pr.val(Math.round(percentage));
lbl.html(Math.round(percentage)+'% uploaded.');
},false);
ajax.addEventListener('load',function(evt){
lbl.html(evt.target.responseText);
pr.val(0);
},false);
ajax.addEventListener('error',function(evt){
lbl.html('upload failed');
pr.val(0);
},false);
ajax.addEventListener('abort',function(evt){
lbl.html('upload aborted');
pr.val(0);
},false);
ajax.open('POST',url);
ajax.send(data);
//again stop form submition (optional)
return false;
});

Drag and drop files (without live upload) / Sending e.dataTransfer.files through form

I'm working on a new drag-and-drop files plugin, however , unlike all those plugins out there
I don't want it to upload it as it "dropped".
The idea is:
1.The user drags and drops the files.
2.(Some Magic)
3.The user submit the form and only than those files getting uploaded to the server
I tried appending the file's name to the form as an input type=hidden , however I can't do anything with it on the server side (php);
Can I append the file's details to a input type=file field some how so the browser will "think" the file has been selected via regular file input field
My js:
$('#drop-zone').bind('drop', function(e) {
// This variable represents the files that have been dragged
// into the drop area
var files = e.dataTransfer.files;
$('#uploaded-list').show();
// For each file
$.each(files, function(index, file) {
/* What can I do in here?*/
});
});
Thanks.
Maybe you've already solved this, but I figured this out today.
This would not be IE9- compliant (but then again, neither are drag and drop files), but you could store the dataTransfer.files data, and when you're ready to submit, create a FormData from the form and append the files.
var formData = new FormData(html_element_of_form); //not a jq obj!
for (var i = 0; i < files.length; i++) {
formData.append('file', files[i]);
}
// now post a new XHR request
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function () {
if (xhr.status === 200) {
console.log('all done: ' + xhr.status);
} else {
console.log('blarrghhhhh...');
}
};
xhr.send(formData);

Uploading files using XMLHttpRequest

I'm trying to use a drag and drop plugin in javascript to upload files using ajax.
<script>
DnD.on('#drop-area', {
'drop': function (files, el) {
el.firstChild.nodeValue = 'Drag some files here.';
var names = [];
[].forEach.call(files, function (file, i) {
names.push(file.name + ' (' + file.size + ' bytes)');
var xhr = new XMLHttpRequest();
xhr.open('POST','upload.php');
xhr.setRequestHeader("Content-type", "multipart/form-data");
xhr.send(file);
console.log(xhr.responseText);
});
document.querySelector('#dropped-files p i').firstChild.nodeValue = names.join(', ');
}
});
</script>
And here's upload.php:
<?php
print_r($_POST);
?>
Basically I haven't written the script to upload the file yet because I'm still figuring out how can I have access to the data that I've sent through JavaScript. Can you guide me on what to do next? How do I access the file from upload.php.
Try to use FormData instead of xhr:
var formData = new FormData();
formData.append("thefile", file);
xhr.send(formData);
You have access to your file with this array:
<?php var_dump($_FILES["thefile"]); ?>
See more: http://www.w3schools.com/php/php_file_upload.asp

Categories