I am working on some legacy code which is using Asp.net and ajax where we do one functionality to upload a pdf. To upload file our legacy code uses AjaxUpload, but I observed some weird behavior of AjaxUpload where onComplete event is getting called before actual file got uploaded by server side code because of this though the file got uploaded successfully still user gets an error message on screen saying upload failed.
And here the most weird thins is that same code was working fine till last week.
Code:
initFileUpload: function () {
debugger;
new AjaxUpload('aj-assetfile', {
action: '/Util/FileUploadHandler.ashx?type=asset&signup=False&oldfile=' + assetObj.AssetPath + '&as=' + assetObj.AssetID,
//action: ML.Assets.handlerPath + '?action=uploadfile',
name: 'AccountSignupUploadContent',
onSubmit: function (file, ext) {
ML.Assets.isUploading = true;
ML.Assets.toggleAsfMask(true);
// change button text, when user selects file
$asffile.val('Uploading');
$astfileerror.hide();
// If you want to allow uploading only 1 file at time,
// you can disable upload button
this.disable();
// Uploding -> Uploading. -> Uploading...
ML.Assets.interval = window.setInterval(function () {
var text = $asffile.val();
if (text.length < 13) {
$asffile.val(text + '.');
} else {
$asffile.val('Uploading');
}
}, 200);
//if url field block is visible
if ($asseturlbkl.is(':visible')) {
$asfurl.val(''); //reset values of url
$asfurl.removeClass('requiref error'); //remove require field class
$asfurlerror.hide(); //hide errors
}
},
onComplete: function (file, responseJSON) {
debugger;
ML.Assets.toggleAsfMask(false);
ML.Assets.isUploading = false;
window.clearInterval(ML.Assets.interval);
this.enable();
var success = false;
var responseMsg = '';
try {
var response = JSON.parse(responseJSON);
if (response.status == 'success') { //(response.getElementsByTagName('status')[0].textContent == 'success') {
success = true;
} else {
success = false;
responseMsg = ': ' + response.message;
}
} catch (e) {
success = false;
}
if (success) {
assetObj.AssetMimeType = response.mimetype;
$asffile.val(response.path);
$asffile.valid(); //clear errors
ML.Assets.madeChanges();
if (ML.Assets.saveAfterUpload) { //if user submitted form while uploading
ML.Assets.saveAsset(); //run the save callback
}
} else { //error
assetObj.AssetMimeType = "";
$asffile.val('');
$astfileerror.show().text('Upload failed' + responseMsg);
//if url field block is visible and type is not free offer.
if ($asseturlbkl.is(':visible') && this.type !== undefined && assetObj.AssetType != this.type.FREEOFFER) {
$asfurl.addClass('requiref'); //remove require field class
}
ML.Assets.hideLoader();
}
}
});
}
I was facing the same issue but I fixed it with some minor change in plugin.
When “iframeSrc” is set to “javascript:false” on https or http pages, Chrome now seems to cancel the request. Changing this to “about:blank” seems to resolve the issue.
Old Code:
var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
New Code with chagnes:
var iframe = toElement('<iframe src="about:blank;" name="' + id + '" />');
After changing the code it's working fine. I hope it will work for you as well. :)
Reference (For more details): https://www.infomazeelite.com/ajax-file-upload-is-not-working-in-the-latest-chrome-version-83-0-4103-61-official-build-64-bit/
Related
The situation
I have a page in which I have multiple forms keeping track of the attendance and one progress_update.
On submit of the progress_update form I have got it so that ajax sends the attendance form submissions separately having used the preventdefault() method to stop the original submission, however I would like to on the condition that no errors were returned by the ajax methods allow the original submission that was originally prevented.
What I have so far:
The ajax function:
function send_attendance(name, lesson, form_id, i) {
var url = '/attendance/' + name + '/' + lesson
$('#error-' + i).hide('slow')
$('#error-' + i).html('')
$.ajax({
type: "POST",
url: url,
data: {
attended: $('#attended' + i).val(),
score: $('#score' + i).val(),
writing: $('#writing' + i).val(),
speaking: $('#speaking' + i).val()},
success: function(data) {
if (data.data.message == undefined) {
allow=false;
if (data.data.score[1] == undefined) {
var error_data = data.data.score[0]
} else {
var error_data = data.data.score[1]
}
$('#error-' + i).show('slow')
$('#error-' + i).html('<p style="color:red;">' + error_data + '</p>')
} else {
console.log(data.data.message) // display the returned data in the console.
}
}
});
}
The Intention:
The intention behind this ajax is to send the forms to a separate route for validation and then on success "receiving data.data.message == 'submitted'" pass to the next form in the loop, while on error set the allow variable to false and display the message in hopes to prevent the final form being submitted at the same time.
The call:
$('#update_form').submit(function (e) {
var allow = true;
for (var i = 0; i < studentcount ; i++) {
send_attendance(name=st[i], lesson=lesson, form_id='attendance-' + i, i=i)
}
if (allow == true){
} else {
e.preventDefault();
}
});
The Problem
In doing what I have done I have ended up with a situation of it either submits the ajax submitted forms and that is that preventing the submit form or it submits the form whether errors occured in the ajax that need to be displayed, now how do I get this to work in the way expected? I have tried the methods involved in these previous questions:
How to reenable event.preventDefault?
How to unbind a listener that is calling event.preventDefault() (using jQuery)?
which revolve around using bind and unbind but this doesn't seem to work as needed and results in a similar error.
Any advice would be greatly appreciated.
Edit:
I have adjusted the code based on the comment below to reflect, however it still seems to be evaluating the allow before the ajax have completed. either that or the ajax function isn't changing the allow variable which is set in the submit() call how could i get this to change the allow and evaluate it after the ajax calls are complete?
The Ajax call
function send_attendance(name, lesson, form_id, i) {
var url = '/attendance/' + name + '/' + lesson
$('#error-' + i).hide('slow')
$('#error-' + i).html('')
var form = $('#' + form_id)
$.ajax({
type: "POST",
url: url,
data: $('#'+ form_id).serialize(),
context: form,
success: function(data) {
console.log('done')
if (data.data.message == undefined) {
allow = false;
if (data.data.score[1] == undefined) {
var error_data = data.data.score[0]
} else {
var error_data = data.data.score[1]
}
$('#error-' + i).show('slow')
$('#error-' + i).html('<p style="color:red;">' + error_data + '</p>')
} else {
console.log(data.data.message) // display the returned data in the console.
}
}
});
The function is being called here:
$('#update_form').submit(function (e) {
e.preventDefault();
var allow = true;
var deferreds = [];
for (var i = 0; i < studentcount ; i++) {
deferreds.push(
send_attendance(st[i], lesson, 'attendance-' + i, i));
}
$.when(...deferreds).then(function() {
if (allow == true){
console.log('True')
} else {
console.log('False')
}
});
I also tried:
$('#update_form').submit(function (e) {
e.preventDefault();
var allow = true;
var deferreds = [];
for (var i = 0; i < studentcount ; i++) {
deferreds.push(
send_attendance(st[i], lesson, 'attendance-' + i, i));
}
$.when.apply(deferreds).done(function() {
if (allow == true){
console.log('True')
} else {
console.log('False')
}
});
I am trying to use the JavaScript/jQuery Ajax File Uploader by Jordan Feldstein available at https://github.com/jfeldstein/jQuery.AjaxFileUpload.js
It is an older library but still very popular due to it's simplicity and for being so lightweight (around 100 lines) and you can attach it to a single form input filed and it simply works! You select a file as normal with the form inut filed and on selection it instantly uploads using AJAX and returns the uploaded file URL.
This makes the library good for my use where I am uploading a file inside a modal window which is also generate with AJAX and I have used this library in many similar projects.
My backend is using PHP and Laravel and that is where my issue seems to be.
My test script works but when I implement it into my Laravel app it returns this error....
ERROR: Failed to write data to 1439150550.jpg, check permissions
This error is set in my controller below when this code is not retuning a value...
$result = file_put_contents( $folder . '/' .$filename, file_get_contents('php://input') );
So perhaps this part file_get_contents('php://input') does not contain my file data?
It does create the proper directory structure and even a file which is /uploads/backing/2015/08/1439150550.jpg
The 1439150550.jpg is a timestamp of when the upload took place. It create this file in the proper location however the file created has no content and is 0 bytes!
Below is my Laravel Controller action which handles the back-end upload and below that the JavaScript....
PHP Laravel Controller Method:
public function uploadBackingStageOneFile(){
// Only accept files with these extensions
$whitelist = array('ai', 'psd', 'svg', 'jpg', 'jpeg', 'png', 'gif');
$name = null;
$error = 'No file uploaded.';
$destination = '';
//DIRECTORY_SEPARATOR
$utc_str = gmdate("M d Y H:i:s", time());
$utc = strtotime($utc_str);
$filename = $utc . '.jpg';
$folder = 'uploads/backing/'.date('Y') .'/'.date('m');
//if Directory does not exist, create it
if(! File::isDirectory($folder)){
File::makeDirectory($folder, 0777, true);
}
// Save Image to folder
$result = file_put_contents( $folder . '/' .$filename, file_get_contents('php://input') );
if (!$result) {
Log::info("ERROR: Failed to write data to $filename, check permissions");
return "ERROR: Failed to write data to $filename, check permissions\n";
}
$url = $folder . '/' . $filename;
return Response::json(array(
'name' => $name,
'error' => $error,
'destination' => $url
));
}
JavaScript AJAX FIle Upload LIbrary
/*
// jQuery Ajax File Uploader
//
// #author: Jordan Feldstein <jfeldstein.com>
// https://github.com/jfeldstein/jQuery.AjaxFileUpload.js
// - Ajaxifies an individual <input type="file">
// - Files are sandboxed. Doesn't matter how many, or where they are, on the page.
// - Allows for extra parameters to be included with the file
// - onStart callback can cancel the upload by returning false
Demo HTML upload input
<input id="new-backing-stage-1-file" type="file">
Demo JavaScript to setup/init this lbrary on the upload field
$('#new-backing-stage-1-file').ajaxfileupload({
'action': '/upload.php',
'params': {
'extra': 'info'
},
'onComplete': function(response) {
console.log('custom handler for file:');
alert(JSON.stringify(response));
},
'onStart': function() {
if(weWantedTo) return false; // cancels upload
},
'onCancel': function() {
console.log('no file selected');
}
});
*/
(function($) {
$.fn.ajaxfileupload = function(options) {
var settings = {
params: {},
action: '',
onStart: function() { },
onComplete: function(response) { },
onCancel: function() { },
validate_extensions : true,
valid_extensions : ['gif','png','jpg','jpeg'],
submit_button : null
};
var uploading_file = false;
if ( options ) {
$.extend( settings, options );
}
// 'this' is a jQuery collection of one or more (hopefully)
// file elements, but doesn't check for this yet
return this.each(function() {
var $element = $(this);
// Skip elements that are already setup. May replace this
// with uninit() later, to allow updating that settings
if($element.data('ajaxUploader-setup') === true) return;
$element.change(function()
{
// since a new image was selected, reset the marker
uploading_file = false;
// only update the file from here if we haven't assigned a submit button
if (settings.submit_button == null)
{
upload_file();
}
});
if (settings.submit_button == null)
{
// do nothing
} else
{
settings.submit_button.click(function(e)
{
// Prevent non-AJAXy submit
e.preventDefault();
// only attempt to upload file if we're not uploading
if (!uploading_file)
{
upload_file();
}
});
}
var upload_file = function()
{
if($element.val() == '') return settings.onCancel.apply($element, [settings.params]);
// make sure extension is valid
var ext = $element.val().split('.').pop().toLowerCase();
if(true === settings.validate_extensions && $.inArray(ext, settings.valid_extensions) == -1)
{
// Pass back to the user
settings.onComplete.apply($element, [{status: false, message: 'The select file type is invalid. File must be ' + settings.valid_extensions.join(', ') + '.'}, settings.params]);
} else
{
uploading_file = true;
// Creates the form, extra inputs and iframe used to
// submit / upload the file
wrapElement($element);
// Call user-supplied (or default) onStart(), setting
// it's this context to the file DOM element
var ret = settings.onStart.apply($element, [settings.params]);
// let onStart have the option to cancel the upload
if(ret !== false)
{
$element.parent('form').submit(function(e) { e.stopPropagation(); }).submit();
}
}
};
// Mark this element as setup
$element.data('ajaxUploader-setup', true);
/*
// Internal handler that tries to parse the response
// and clean up after ourselves.
*/
var handleResponse = function(loadedFrame, element) {
var response, responseStr = $(loadedFrame).contents().text();
try {
//response = $.parseJSON($.trim(responseStr));
response = JSON.parse(responseStr);
} catch(e) {
response = responseStr;
}
// Tear-down the wrapper form
element.siblings().remove();
element.unwrap();
uploading_file = false;
// Pass back to the user
settings.onComplete.apply(element, [response, settings.params]);
};
/*
// Wraps element in a <form> tag, and inserts hidden inputs for each
// key:value pair in settings.params so they can be sent along with
// the upload. Then, creates an iframe that the whole thing is
// uploaded through.
*/
var wrapElement = function(element) {
// Create an iframe to submit through, using a semi-unique ID
var frame_id = 'ajaxUploader-iframe-' + Math.round(new Date().getTime() / 1000)
$('body').after('<iframe width="0" height="0" style="display:none;" name="'+frame_id+'" id="'+frame_id+'"/>');
$('#'+frame_id).get(0).onload = function() {
handleResponse(this, element);
};
// Wrap it in a form
element.wrap(function() {
return '<form action="' + settings.action + '" method="POST" enctype="multipart/form-data" target="'+frame_id+'" />'
})
// Insert <input type='hidden'>'s for each param
.before(function() {
var key, html = '';
for(key in settings.params) {
var paramVal = settings.params[key];
if (typeof paramVal === 'function') {
paramVal = paramVal();
}
html += '<input type="hidden" name="' + key + '" value="' + paramVal + '" />';
}
return html;
});
}
});
}
})( jQuery );
My JavaScript usage of the above library:
// When Modal is shown, init the AJAX uploader library
$("#orderModal").on('shown.bs.modal', function () {
// upload new backing file
$('#new-backing-stage-1-file').ajaxfileupload({
action: 'http://timeclock.hgjghjg.com/orders/orderboards/order/uploadbackingimage',
params: {
extra: 'info'
},
onComplete: function(response) {
console.log('custom handler for file:');
console.log('got response: ');
console.log(response);
console.log(this);
//alert(JSON.stringify(response));
},
onStart: function() {
//if(weWantedTo) return false; // cancels upload
console.log('starting upload');
console.log(this);
},
onCancel: function() {
console.log('no file selected');
console.log('cancelling: ');
console.log(this);
}
});
});
The problem is like you said, file_get_contents('php://input') does not contain your file data.
jQuery.AjaxFileUpload plugin wrap file input element with a form element that contains enctype="multipart/form-data" attribute. 1
From php documentation: 2
php://input is not available with enctype="multipart/form-data".
I have this ajax call here in a script tag at the bottom of my page. Everything works fine! I can set a breakpoint inside the 'updatestatus' action method in my controller. My server gets posted too and the method gets called great! But when I put the javascript inside a js file the ajax call doesn't hit my server. All other code inside runs though, just not the ajax post call to the studentcontroller updatestatus method.
<script>
$(document).ready(function () {
console.log("ready!");
alert("entered student profile page");
});
var statusdropdown = document.getElementById("enumstatus");
statusdropdown.addEventListener("change", function (event) {
var id = "#Model.StudentId";
var url = '#Url.Action("UpdateStatus", "Student")';
var status = $(this).val();
$.post(url, { ID: id, Status: status }, function (data) {
// do something with the returned value e.g. display a message?
// for example - if(data) { // OK } else { // Oops }
});
var e = document.getElementById("enumstatus");
if (e.selectedIndex == 0) {
document.getElementById("statusbubble").style.backgroundColor = "#3fb34f";
} else {
document.getElementById("statusbubble").style.backgroundColor = "#b23f42";
}
}, false);
</script>
Now I put this at the bottom of my page now.
#section Scripts {
#Scripts.Render("~/bundles/studentprofile")
}
and inside my bundle.config file it looks like this
bundles.Add(new ScriptBundle("~/bundles/studentprofile").Include(
"~/Scripts/submitstatus.js"));
and submitstatus.js looks like this. I know it enters and runs this code because it I see the alert message and the background color changes. So the code is running. Its just not posting back to my server.
$(document).ready(function () {
console.log("ready!");
alert("submit status entered");
var statusdropdown = document.getElementById('enumstatus');
statusdropdown.addEventListener("change", function (event) {
var id = "#Model.StudentId";
var url = '#Url.Action("UpdateStatus", "Student")';
var status = $(this).val();
$.post(url, { ID: id, Status: status }, function (data) {
// do something with the returned value e.g. display a message?
// for example - if(data) { // OK } else { // Oops }
});
var e = document.getElementById('enumstatus');
if (e.selectedIndex == 0) {
document.getElementById("statusbubble").style.backgroundColor = "#3fb34f";
} else {
document.getElementById("statusbubble").style.backgroundColor = "#b23f42";
}
}, false);
});
In the console window I'm getting this error message.
POST https://localhost:44301/Student/#Url.Action(%22UpdateStatus%22,%20%22Student%22) 404 (Not Found)
Razor code is not parsed in external files so using var id = "#Model.StudentId"; in the main view will result in (say) var id = 236;, in the external script file it will result in var id = '#Model.StudentId'; (the value is not parsed)
You can either declare the variables in the main view
var id = "#Model.StudentId";
var url = '#Url.Action("UpdateStatus", "Student")';
and the external file will be able to access the values (remove the above 2 lines fro the external script file), or add them as data- attributes of the element, for example (I'm assuming enumstatus is a dropdownlist?)
#Html.DropDownListFor(m => m.enumStatus, yourSelectList, "Please select", new { data_id = Model.StudentId, data_url = Url.Action("UpdateStatus", "Student") })
which will render something like
<select id="enumStatus" name="enumStatus" data-id="236" data-url="/Student/UpdateStatus">
Then in the external file script you can access the values
var statusbubble = $('#statusbubble'); // cache this element
$('#enumStatus').change(function() {
var id = $(this).data('id');
var url = $(this).data('url');
var status = $(this).val();
$.post(url, { ID: id, Status: status }, function (data) {
....
});
// suggest you add/remove class names instead, but if you want inline styles then
if (status == someValue) { // the value of the first option?
statusbubble.css('backgroundColor', '#3fb34f');
} else {
statusbubble.css('backgroundColor', '#b23f42');
};
});
Here is the jquery code that is the problem. I wanted for the ajax to send json data to the server and then submit the form. If I don't have the when and done clause then it's possible for submission to be done before the ajax and will not be able to retrieve success or error in time.
function deleteImage(button)
{
//There is only one image that is a sibling of the delete button
var image = $(button).siblings(".MultiFile-image")[0];
var groupId = $(image).data("modelId");
var imgId = $(image).data("id");
var imgSrc = $(image).attr("src");
//Delete the image view after the removed button is clicked but the data to be sent to the server for deletion is already stored
$(button).parent(".MultiFile-label").remove();
var imageToDelete = {imgId:imgId, imgSrc:imgSrc, groupId:groupId};
var imageJSON = '{"imageToDelete":' + JSON.stringify(imageToDelete) + "}";
//This is needed to check whether ajax has been executed before submission
var sentImageData = false;
$("form").submit(function(e) {
//Stop submission, need to send data through ajax first, will submit after ajax is executed later.
if(!sentImageData)
{
e.preventDefault();
//Send the images for deletion only when the form has been submitted
//For some reason this code is never executed and go immediately to the end of this method
$.when(sendImageData(imageJSON)).done(function(jqXHR) {
if(jqXHR.readyState == 4 && jqXHR.status == 200)
{
sentImageData = true;
$("form").submit();
}
else
{
console.log(jqXHR);
sentImageData = false;
}
}); //For some reason the debugger skips to here and return is undefined
}
//If executed is true, send the form as normal
});
}
/**
* #var imageJSON the image json data that will be sent to the server to delete the image
* #returns {#exp;$#call;ajax} return XMLHttpRequest of the ajax
*/
function sendImageData(imageJSON)
{
return $.ajax({
type: 'POST',
data: imageJSON,
dataType: 'JSON',
url: "index.php?r=artworkGroup/deleteArtwork",
});
}
Thank you, I would much appreciate the help from the community on this problem :)
EDIT: Here is the action that handles this ajax code. an example of json is: "{"imageToDelete":{"imgId":2,"imgSrc":"upload_file/artwork/1-New_Artwork_Group/12861274.jpg","groupId":2}}"
public function actionDeleteArtwork() {
$noError = false;
if(isset($_POST["imageToDelete"]))
{
$imageArray = $_POST["imageToDelete"];
//Delete every image retrieved by post
foreach($imageArray as $image)
{
$transaction = Yii::app()->db->beginTransaction();
try{
$imageToDelete = json_decode($image);
$model = $this->loadModel($imageToDelete->groupId);
$artworkToDelete = $model->artworks->loadModel($imageToDelete->id);
if($imageToDelete->imgSrc == $artworkToDelete->imgSrc)
{
$artworkToDelete->delete();
if(file_exists($imageToDelete->imgSrc))
{
unlink($imgToDelete->imgSrc);
}
}
else
{
$hasError = true;
}
$transaction->commit();
}
catch(Exception $e)
{
$transaction->rollback();
$hasError = true;
}
//Delete the image files if there are no errors and that the file exists, otherwise just ignore
if(file_exists($imageToDelete->imgSrc) && $noError)
{
unlink($imageToDelete->imgSrc);
}
}
}
}
You have omitted url from your ajax request. that means it is going to hit your current page url. That may be triggering timeout.
and Timeout is kind of error in $.ajax. thats why your
sendImageData(imageJSON)
is returning you false. and by consequence of it your .done() is not getting executed.
I've searched for hours to get a solution for my problem. But I have to ask the community now. I've programmed an ajax file upload system. Here is the Javascript:
var handleUpload = function(event) {
event.preventDefault();
event.stopPropagation();
var fileInput = document.getElementById('fileAvatar');
var data = new FormData();
data.append('ajax', true);
data.append('avatar', fileInput.files[0]);
var request = new XMLHttpRequest();
request.upload.addEventListener('error', function(event) {
alert('Upload Failed');
});
request.addEventListener('readystatechange',function(event) {
if (this.readyState == 4) {
if (this.status == 200) {
var uploaded = this.response.split("|");
// DO SOME ERROR HANDLING IN THIS AREA
if (uploaded[0] == 'upload_success') {
$('.avatarCropImage').attr('src','<?php echo USERFILES;?><?php echo $log_username; ?>/' + uploaded[1]);
$('.cropInput').attr('type',uploaded[2]);
showPopup('cropAvatar');
/************************/
/***** Problem Area *****/
/************************/
} else {
showPopup('errorNotification');
_('popupError').innerHTML = 'Something went wrong. Please try again.';
}
} else {
alert('Error' + this.status);
}
}
});
request.open('POST','<?php echo $url_data; ?>');
request.setRequestHeader('Cashe-Control', 'no-cashe');
request.send(data);
}
window.addEventListener('load', function() {
var submit = document.getElementById('submitAvatar');
submit.addEventListener('click',handleUpload);
});
The file upload works fine and as you can see, after the file was uploaded I push the uploaded image into a popup called cropAvatar.
Then the user has to crop an area to get a thumbnail of his avatar. If he selects an area and clicks on the Crop-Button, the Crop-Function will be run:
function cropImage() {
var top = $('.cropBox').position().top - 3;
var left = $('.cropBox').position().left - 3;
var width = $('.cropBox').width();
var height = $('.cropBox').height();
var src = $('.avatarCropImage').attr('src');
var type = $('.cropInput').attr('type');
var ajax = ajaxObj("POST", "<?php echo $url_data; ?>");
ajax.onreadystatechange = function() {
if(ajaxReturn(ajax) == true) {
if (ajax.responseText == "") {
$('.buttonClose').click();
$('.avatarImage').attr('src',src);
$('.cropAvatar').css('display','none');
} else {
alert(ajax.responseText);
showPopup('errorNotification');
_('popupError').innerHTML = 'Something went wrong. Please try again.';
}
}
}
ajax.send("action=avatar&top="+top+"&left="+left+"&width="+width+"&height="+height+"&src="+src+"&type="+type);
}
This also works pretty well. The problem now is that the user can bypass the Crop-Function when he reloads the page. Do you have any solution for that?
I also tried to fix this problem by entering the following code into the Problem Area:
// cropImage() is the Crop-Function
window.unload = cropImage();
Thanks for helping.
Don't save the avatar until the user has done the cropping step.
Leave the file as a dangling temp file until the user has completed the whole upload wizard.
I can come up with a similar scenario:
When you paste a link into a Facebook post, Facebook will give you a thumbnail image for the link. What if you then cancel the post? Where does the thumbnail go, or actually, where has it been since there was no post yet? It's all in a temporary structure until you commit, ie. complete the post.