In my case I manipulate an image using client-side javascript, because I want to scale it before uploading it:
$("#base_img_to_compress").on("change", (e) => {
resizeImage(source, 1440, 1080).then((imgData) => {
appendToFormHiddenImageInput("base_img_data",imgData);
});
});
$("#upload_form").on("submit",(e)=>{
$.ajax({
method:"POST",
//How I can configure the postdata here ?
})
});
function appendToFormHiddenImageInput(name, imgData) {
const child = $("#upload_form").children("input[name=" + name + "]");
if (child.length > 0) {
child.remove();
}
const input = document.createElement('input');
input.type = "hidden";
input.setAttribute("type", "hidden");
input.setAttribute("name", name);
input.setAttribute("value", imgData);
$("#upload_form").append(input);
}
function resizeImage(element, width, height) {
const element_file = element.files[0];
return imageConversion.compressAccurately(element_file, {
quality: 0.7,
type: "image/png",
width: width,
height: height,
size: 80
}).then((file) => {
return imageConversion.filetoDataURL(file);
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/WangYuLue/image-conversion/build/conversion.js"></script>
<form id="upload_form">
<button type="submit" id="savebtn">Save me</button>
</form>
<input id="base_img_to_compress" type="file" />
The php script that handles the upload is rather simple:
$file = $_FILES['base_img_data'];
if($file['tmp_name']){
echo "FILE UPLOADED";
exit;
}
echo "FILE NOT UPLOADED";
But in order for $_FILES
to work need the data to be uploaded as multipart/form-data
where the base_img_data
must be encoded as base64 string. Is there a way to do this at my javascript without using an <input type="file" />
input element as w3schools say so?
In other words the part that troubles me is this piece of javascript:
$("#upload_form").on("submit",(e)=>{
$.ajax({
method:"POST",
//How I can configure the postdata here ?
})
});
I mean, I need to populate the ajax accordingly in order to emulate a multipart form upload without using an input field that has file
attribute. But in my case, I use hidden input fields with values encoded as base64, therefore the PHP won't recognize the incoming uploaded file using $_FILES
magic variable.
You have to manually build your own FormData
object. This is an approach:
const formData = new FormData();
$("#upload_form :input").each((index,element)=>{
let value = $(element).val();
if($(element).attr('type') == "hidden"){
formData.append($(element).attr('name'),DataURIToBlob(value),`myfile_${index}.png`);
}
formData.append($(element).attr('name'),value);
});
function DataURIToBlob(dataURI) {
const splitDataURI = dataURI.split(',')
const byteString = splitDataURI[0].indexOf('base64') >= 0 ? atob(splitDataURI[1]) : decodeURI(splitDataURI[1])
const mimeString = splitDataURI[0].split(':')[1].split(';')[0]
const ia = new Uint8Array(byteString.length)
for (let i = 0; i < byteString.length; i++){
ia[i] = byteString.charCodeAt(i)
}
return new Blob([ia], { type: mimeString })
}
Then do the ajax call like that:
$.ajax({
method:"POST",
data: formData,
url: "./script.php",
processData: false,
contentType: false,
success: ()=>{
alert("BANZAI");
}
})
In your example a complete submit method is:
$("#upload_form").on("submit",(e)=>{
e.preventDefault();
const formData = new FormData();
$("#upload_form :input").each((index,element)=>{
let value = $(element).val();
if($(element).attr('type') == "hidden"){
console.log(formData);
formData.append($(element).attr('name'),DataURIToBlob(value),`myfile_${index}.png`);
}
formData.append($(element).attr('name'),value);
});
$.ajax({
method:"POST",
data: formData,
url: "./script.php",
processData: false,
contentType: false,
success: ()=>{
alert("BANZAI");
}
})
});
function DataURIToBlob(dataURI) {
const splitDataURI = dataURI.split(',')
const byteString = splitDataURI[0].indexOf('base64') >= 0 ? atob(splitDataURI[1]) : decodeURI(splitDataURI[1])
const mimeString = splitDataURI[0].split(':')[1].split(';')[0]
const ia = new Uint8Array(byteString.length)
for (let i = 0; i < byteString.length; i++){
ia[i] = byteString.charCodeAt(i)
}
return new Blob([ia], { type: mimeString })
}
Please notice that upon ajax alongside with data
option I use these options as well:
processData: false,
contentType: false,
If they are not set to false then the upload won't happen.
Upon placing input differentiate it using a data attribute, for example, data-file
once you dynamically add it. In your example an approach should be to replace the following function:
function appendToFormHiddenImageInput(name, imgData) {
const child = $("#upload_form").children("input[name=" + name + "]");
if (child.length > 0) {
child.remove();
}
const input = document.createElement('input');
input.type = "hidden";
input.setAttribute("type", "hidden");
input.setAttribute("name", name);
input.setAttribute("value", imgData);
input.setAttribute("data-file",true); // <<< This appends the distingushing indicator
$("#upload_form").append(input);
}
The use the following submit method:
$("#upload_form").on("submit",(e)=>{
e.preventDefault();
const formData = new FormData();
$("#upload_form :input").each((index,element)=>{
let value = $(element).val();
if($(element).attr('type') == "hidden" && $(element).data('file') === true ){
console.log(formData);
formData.append($(element).attr('name'),DataURIToBlob(value),`myfile_${index}.png`);
}
formData.append($(element).attr('name'),value);
});
$.ajax({
method:"POST",
data: formData,
url: "./script.php",
processData: false,
contentType: false,
success: ()=>{
alert("BANZAI");
}
})
});
function DataURIToBlob(dataURI) {
const splitDataURI = dataURI.split(',')
const byteString = splitDataURI[0].indexOf('base64') >= 0 ? atob(splitDataURI[1]) : decodeURI(splitDataURI[1])
const mimeString = splitDataURI[0].split(':')[1].split(';')[0]
const ia = new Uint8Array(byteString.length)
for (let i = 0; i < byteString.length; i++){
ia[i] = byteString.charCodeAt(i)
}
return new Blob([ia], { type: mimeString })
}
Pay attention to the line:
if($(element).attr('type') == "hidden" && $(element).data('file') === true )
I also check if the field is an input file or not.
Also pay attention to:
input.setAttribute("data-file",true);
It works because using form-data you make a POST encoded as multipart/form-data
. $_FILES is a parsed value from a body that seems to be a file. In our case we re-constructed the form as multipart one at line:
formData.append($(element).attr('name'),DataURIToBlob(value),`myfile_${index}.png`);
At function appendToFormHiddenImageInput
In my case I set a name with fixed file extension, if you want to manage it manually use the blob type. I could do it like this:
const name = `myfile_${index}`
value = DataURIToBlob(value);
let suffix = 'png'
swich(value.type)
{
// handle suffix here
}
name = `${name}.${suffix}`
formData.append($(element).attr('name'),value,name);
Replacing the:
formData.append($(element).attr('name'),DataURIToBlob(value),`myfile_${index}.png`);
Also, I made sure, that on the php side I had the appropriate value on upload_max_filesize
setting it php.ini
.