verwerkingsindicatie na submit
als nadeel wordt ervaren dat het soms even (hooguit een seconde of 10) duurt voordat de ingevulde gegevens verwerkt zijn na submit.
niet ieder kan het gewenste geduld opbrengen, sommigen zijn in staat om 5 x in een minuut de submitknop te bedienen.
gevolg: dubbele inschrijvingen bij mij en evenzovele bevestigingsmail bij de inschrijver, etc
vraag (na veel vergeefs zoeken): is er een manier om de submitknop te 'bevriezen' tot de verwerking klaar is en een bevestigingsscherm verschijnt cq een indicatie wordt gegeven dat de verwerking loopt?
prima zou ik vinden iets als: de uploadknop blijft azuur zolang de upload in een move_uploaded_file() loopt.
Maar ik vraag me af of het niet handiger is om te kijken waarom het zo lang duurt? Een upload hoeft niet eens lang te duren. Op mijn server wordt een 10 MB foto al in een seconde al verwerkt. Of zijn het echt grote bestanden?
https://codepen.io/Xia-lj/pen/ojNKQE
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<script>
function _(el) {
return document.getElementById(el);
}
function uploadFile() {
var file = [];
file = _("files").files[0];
//alert(file.name+" | "+file.size+" | "+file.type);
var formdata = new FormData();
formdata.append("files", file);
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", progressHandler, false);
ajax.addEventListener("load", completeHandler, false);
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", "file_upload_parser.php");
ajax.send(formdata);
}
function progressHandler(event) {
_("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
var percent = (event.loaded / event.total) * 100;
_("progressBar").value = Math.round(percent);
_("status").innerHTML = Math.round(percent) + "% uploaded... please wait";
}
function completeHandler(event) {
_("status").innerHTML = event.target.responseText;
_("progressBar").value = 0;
}
function errorHandler(event) {
_("status").innerHTML = "Upload Failed";
}
function abortHandler(event) {
_("status").innerHTML = "Upload Aborted";
}
</script>
<h2>HTML5 File Upload Progress Bar Tutorial</h2>
<form id="upload_form" enctype="multipart/form-data" method="post">
<input type="file" name="files" id="files" ><br />
<br />
<input type="button" value="submit" onclick="uploadFile()">
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<h3 id="status"></h3>
<p id="loaded_n_total"></p>
</form>
function _(el) {
return document.getElementById(el);
}
function uploadFile() {
var file = [];
file = _("files").files[0];
//alert(file.name+" | "+file.size+" | "+file.type);
var formdata = new FormData();
formdata.append("files", file);
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", progressHandler, false);
ajax.addEventListener("load", completeHandler, false);
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", "file_upload_parser.php");
ajax.send(formdata);
}
function progressHandler(event) {
_("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
var percent = (event.loaded / event.total) * 100;
_("progressBar").value = Math.round(percent);
_("status").innerHTML = Math.round(percent) + "% uploaded... please wait";
}
function completeHandler(event) {
_("status").innerHTML = event.target.responseText;
_("progressBar").value = 0;
}
function errorHandler(event) {
_("status").innerHTML = "Upload Failed";
}
function abortHandler(event) {
_("status").innerHTML = "Upload Aborted";
}
</script>
<h2>HTML5 File Upload Progress Bar Tutorial</h2>
<form id="upload_form" enctype="multipart/form-data" method="post">
<input type="file" name="files" id="files" ><br />
<br />
<input type="button" value="submit" onclick="uploadFile()">
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<h3 id="status"></h3>
<p id="loaded_n_total"></p>
</form>
En dit is het PHP bestand :
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?php
$targetDir = "uploads/";
$allowTypes = array('jpg','png','jpeg','gif');
$fileName = basename( $_FILES['files']['name'] );
$targetFilePath = $targetDir . $fileName;
$fileType = pathinfo( $targetFilePath, PATHINFO_EXTENSION );
if( in_array( $fileType, $allowTypes ) ) {
move_uploaded_file( $_FILES["files"]["tmp_name"], $targetFilePath );
}
?>
$targetDir = "uploads/";
$allowTypes = array('jpg','png','jpeg','gif');
$fileName = basename( $_FILES['files']['name'] );
$targetFilePath = $targetDir . $fileName;
$fileType = pathinfo( $targetFilePath, PATHINFO_EXTENSION );
if( in_array( $fileType, $allowTypes ) ) {
move_uploaded_file( $_FILES["files"]["tmp_name"], $targetFilePath );
}
?>
Gewijzigd op 24/01/2019 15:20:15 door Adoptive Solution
ik heb me denk ik niet duidelijk genoeg geuit: ik heb géén probleem met uploaden, dat heb ik aangehaald als voorbeeld.
waar het mij om ging is de vertraging van de submitknop bij formulieren.
met dank ook voor de verwijzing naar scripts die de submitknop disabelen: was voor mij een nieuwe zoekoptie (zoeken slaagt vooral als je de juiste zoektermen gebruikt...).
heb er enkele uitgeprobeerd, maar punt is dat mijn formulieren na submit een controleslag doorlopen op lege en/of onjuiste velden, en dan heb je niks meer aan een uitgeschakelde submitknop....
heb met de secondewijzer ook nog eens de reactietijd gemeten: max drie seconden, dus dat vraagt weinig geduld, zou je zeggen, maar wellicht is het anders als het druk is....
en niet te vergeten: men is soms gewend om stelselmatig dubbel te klikken, terwijl een enkele klik op de submitknop voldoet.
kortom: toch een leerzame middag, maar ik houd het voorlopig maar op een stevige waarschuwing om niet te enthousiast te klikkeren!
Gewijzigd op 24/01/2019 17:11:25 door derk dejong
Misschien toch eens uitzoeken waarom er een wachttijd is van drie seconden. Zo'n submit kan best in minder dan een seconde verwerkt worden. Ik doe dat met vrij grote bestellingen met veel producten in minder dan een seconde. Wat gebeurt er allemaal in jouw submit moment?
Er zijn ook andere middelen die het "dubbelposten" kunnen helpen voorkomen.
In de eerste plaats is het eigenlijk het beste (en ook het meest overzichtelijk) om het weergeven van het formulier en het verwerken van het formulier echt te scheiden in twee verschillende programmatische acties. Vaak is simpele formulierfunctionaliteit beschikbaar via één (monolitisch) script, bijvoorbeeld inschrijven.php of contact.php. Dus zowel het weergeven + verwerken van dit formulier zitten hierin ingebakken zonder dat er expliciet (door verschillende URLs aan te roepen) onderscheid gemaakt wordt tussen de twee "toestanden" van dit script. Dit wordt vaak intern uitgeplozen op grond van de toestand van bepaalde supergloble variabelen (zoals de waarde van $_SERVER['REQUEST_METHOD'] of het bestaan van $_POST variabelen). Vervolgens wordt dan op grond hiervan de bijbehorende actie uitgevoerd.
Een potentieel risico van deze opzet is dat de pagina kan worden ververst nadat er is gePOST. Dit resulteert in een waarschuwing van je browser (wil je de ingevulde gegevens opnieuw verzenden etc) en als je dan op OK klikt dan wordt de informatie opnieuw gepost. Dit scenario wil je eigenlijk te allen tijde vermijden en dit kun je doen door de verwerkstap in een aparte actie te stoppen. Indien de validatie van de gegevens slaagt worden de gegevens verwerkt en anders word je teruggestuurd naar het formulier met kanttekeningen hoe je deze fouten kunt verbeteren. Indien de validatie en verwerking succesvol was word je na afloop direct doorgestuurd naar een "bedankpagina". Dit stramien is beter bekend als het POST/redirect/GET patroon.
Een andere maatregel die je kunt aanbrengen is een zogenaamd token in je formulier. Hiertoe heb je een mechanisme nodig dat over meerdere pagina's informatie kan onthouden (HTTP is normaal stateless dit betekent dat als je van pagina A navigeert naar pagina B dat je in principe al vergeten bent dat je op pagina A zat). Dit doe je via sessies. Op het moment dat je de formulierpagina laadt stel je in je sessie ($_SESSION) een token met een willekeurige waarde in (zeg X). Tevens voeg je een verborgen veld toe aan je formulier met precies dezelfde waarde (X). Op het moment dat je je formulier submit leg je de waarden in de sessie en de formulierdata weer naast elkaar en als deze overeenkomen ga je verder en anders staak je direct alle activiteit. Ook invalideer je direct dit token in je sessie zodat als er (haast) direct een nieuw POST request achteraan komt dit ook meteen resulteert in het staken van enige vervolgactie. Dit token is eigenlijk voornamelijk / ook bedoeld om geautomatiseerde submits van buitenaf (zogenaamde Cross Site Scripting) te voorkomen, maar dit helpt dus ook tegen dubbelposten.
Het zou natuurlijk wel gebruiksvriendelijk(er) zijn om iemand terug te sturen naar het formulier als een token niet meer bestaat, zonder dat iemand al zijn/haar ingevulde informatie verliest. Het kan namelijk prima het geval zijn dat een sessie op een gegeven moment verloopt en het is dan nogal zuur als je al je ingevulde informatie kwijt bent. Maar dit is een ander probleem dat ook (makkelijk) apart opgelost kan worden.
En tot slot, als een soort van "last line of defense" zou je in de verwerking van je gegevens, als dit aan een database is gekoppeld, het gebruik van transacties kunnen toevoegen. Dit moet je dan wel op de juiste wijze toepassen, maar als je dit doet, dan resulteert dit in een situatie waarop op elk moment maar maximaal één formulierverwerking tegelijkertijd (in één atomaire -ondeelbare- actie) kan plaatsvinden. Dit doe je bijvoorbeeld door in de validatie een controle in te bouwen voor een element dat uniek moet zijn voor elke inschrijving, bijvoorbeeld een e-mailadres. Deze kolom in de database (en daarmee de tabel) kun je dan voor de duur van de verwerking vergrendelen voor invoer zodat de verwerking die dan loopt het alleenrecht heeft om te schrijven in de tabel. Andere (parallele) inschrijvingen zullen dan moeten wachten totdat die verwerking klaar is. En als een andere inschrijvingen dan aan de beurt is dan wordt deze aan dezelfde "vergrendelingscontrole" onderworpen. Als dit dan een duplicaat is van de vorige voltooide inschrijving dan wordt die op grond van de validatieprocedure (met het unieke element) gestaakt nog voordat er iets wordt weggeschreven naar de database. Op deze manier kun je dus op databaseniveau duplicaten voorkomen.
NB: bovengenoemde voorzieningen bevinden zich alle aan de webserverkant. Dit in tegenstelling tot oplossingen als het disablen van de submitknop die aan de gebruikerskant in de browser plaatsvinden. Het is altijd goed/beter om ook of in ieder geval controles aan de webserverkant in te bouwen, omdat deze niet te omzeilen zijn, zoals bij het disablen van knoppen wel het geval is. Vooral als er kwade opzet in het spel is (spambots of wat dan ook) ben je altijd beter af met (extra) voorzieningen aan de webserverkant.
Gewijzigd op 24/01/2019 19:58:23 door Thomas van den Heuvel
leesvoer, leesvoer... er valt nog veel te leren, en dat ga ik dan maar eens doen!