Posting Form Data in Javascript with Fetch - Bug - javascript

I'd like to start by mentioning that I'm using vanilla Javascript and PHP, no jquery. The code that I have posted below is just a fragment. I have not included the PHP file or other code in javascript.
Now, the problem I am having is that no other code besides the form data post runs whenever I click my save button and activate the event. I have a console log at the beginning and end of the event to test if other code runs, and it never does. The form data, in this case a picture, gets posted to the PHP rest API file and stored in a folder as it should, but I do not receive a response in JSON from the PHP file that it is posted to, and my biggest problem is that no other code besides the post request runs in the event of the javascript code. Neither of the console.logs (test 1 and test 2) will appear.
When I test the post request with any other type of data, for instance, JSON, everything works perfectly. All of the code in the event runs, and I can receive responses in JSON from the same PHP file that the request was made to. There's something about posting form data that creates this bug. I hope that I have explained this clearly enough. Thank you for any assistance.
save_bg_btn.addEventListener('click', save_background_picture);
async function save_background_picture(){
console.log("test 1");
const formData = new FormData();
const save_files_background_pic = file_bg_pic.files[0];
const url = 'http://localhost/test/background-cover.php';
formData.append("file_bg_pic", save_files_background_pic);
await post_formdata_request(url, formData)
.then(data =>{
console.log(data);
})
.catch(err => console.log(err));
console.log(test 2);
}
function post_formdata_request(url, formData){
return new Promise((resolve, reject) => {
fetch(url, {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(data => resolve(data))
.catch(err => reject(err));
});
}

Assuming this button is in a form, I think you need to add preventDefault() for the click event otherwise the form will submit and refresh the page. Also, fix the second console.log because that was breaking my tests until I noticed it as well.
async function save_background_picture(e){
e.preventDefault();
// ...rest of the code
console.log("test 2"); // <--- needed quotes
}

html file: test-fetch.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form id="myForm">
<input type="file" id="inpFile">
</form>
</body>
<script>
const inpFile = document.getElementById('inpFile');
const myForm = document.getElementById('myForm');
myForm.addEventListener('change', inpFunction);
async function inpFunction(e){
e.preventDefault();
const endpoint = "http://localhost/php-practice/test-fetch.php";
const formData = new FormData();
formData.append('inpFile', inpFile.files[0]);
fetch(endpoint, {
method: 'post',
body: formData
})
.catch(err => console.log(err));
}
</script>
</html>
And this is the php file: test-fetch.php
<?php
$file = $_FILES['inpFile'];
$fileName = $_FILES['inpFile']['name'];
$fileTmpName = $_FILES['inpFile']['tmp_name'];
$fileSize = $_FILES['inpFile']['size'];
$fileError = $_FILES['inpFile']['error'];
$fileType = $_FILES['inpFile']['type'];
$fileExt = explode('.', $fileName);
$fileActualExt = strtolower(end($fileExt));
$allowed = array('jpg', 'jpeg', 'png', 'pdf');
if(in_array($fileActualExt, $allowed)){
if($fileError === 0){
if($fileSize < 2000000){
$fileNameNew = uniqid('', true).".".$fileActualExt;
$fileDestination = 'images/'.$fileNameNew;
move_uploaded_file($fileTmpName, $fileDestination);
}else{
echo "Your file is too large!";
}
}else{
echo "There was an error uploading your file!";
}
}else{
echo "You cannot upload files of this type!";
}
echo 'Success';
?>
Now, if you add any other code, such as a console log inside the function "inpFunction" it will not run. The only code that will run will be the fetch posting the form data to the php file. This problem is really baffling me.
edit: the php file requires a folder called "images" as that is the path destination of any pictures being posted.

Related

How to write to a file from a javascript function after clicking on a button in html

I'm trying to write really basic code where after a button press on an html file, a .txt file is written locally through a javascript function. Is this impossible to do? I can write to a file with just the javascript file but not when trying with both.
<!DOCTYPE html>
<html>
<head>
<title>Tic Tac Toe</title>
<script type = "text/javascript" src="test.js"></script>
</head>
<body>
<input type="button" onclick="javascript: myFunction()" value="Display"/>
</body>
</html>
index.html
function myFunction() {
const fs = require('fs')
// Data which will write in a file.
let data = "Learning how to write in a file."
// Write data in 'Output.txt' .
fs.writeFile('output.txt', data, (err) => {
// In case of a error throw err.
if (err) throw err;
})
}
test.js
You can use the File API, which implements the Blob API, like this
const writeToTextFile = (text, fileName) => {
let textFile = null;
const makeTextFile = (text) => {
const data = new Blob([text], {
type: 'text/plain',
});
if (textFile !== null) {
window.URL.revokeObjectURL(textFile);
}
textFile = window.URL.createObjectURL(data);
return textFile;
};
const link = document.createElement('a');
link.setAttribute('download', fileName);
link.href = makeTextFile(text);
link.click();
};
const data = 'Learning how to write in a file.';
writeToTextFile(data, 'output.txt');
If you are planning use browser to create into a server a file directly, it hopefully is not possible. Imagine someone malicious send a script with instructions to erase all server, for instance.
const fs = require('fs')
this is a library to usage into a server side, node js create locally files because it is a server side engine.
So, the correct flow is, your browser make a request to a server, in server side you validate that request, so create a file.

How to show alerts on bad request, but redirect on success?

I have a registration form, the user is being redirected to home.php after success (works)
But also all the 'alerts/errors' which are echos in PHP, after submit, will redirect to register.php and show the error in blank white page.
(How do i display them to <div class="msg"> position?)
<script>
document.querySelector(".register form").addEventListener("submit", async (e) => {
e.preventDefault()
const form = e.target
const body = new FormData(form)
// fetch is much easier to use than XHR
const res = await fetch(form.action, {
method: "POST",
headers: {accept: "application/json", // let PHP know what type of response we want},
body})
const data = await res.json()
if (res.ok) {
location.href = data.location
} else if (res.status === 400) {
document.querySelector('.msg').textContent = data.message
// also do something with data.errors maybe
}
})
</script>
<body>
<div class="msg"></div> <!--position for error/ wrong pass etc-->
register.php
Based off that, please provide a correct code snippet in order to mark this as resolved.
It would probably make your life quite a bit easier if you always returned JSON from your PHP, rather than sometimes HTML as well.
For examples, when checking the errors at the top of register.php, you should return JSON objects --- e.g.
{error: "Email is not valid!"}
rather than their HTML equivilents.
This means that in your fetch, you'll now always be able to get the JSON content (currently, you'd probably get an error in your browser's debug console if one of those messages came back, as it's not valid JSON). Then, in your JavaScript, you can just detect this and switch however you want:
if (data.error) { // If there is an error
document.querySelector(".msg").textContent = data.error;
}
else if (data.location) { // If we want to redirect the user somewhere
window.location.href = "./" + data.location;
}

Sending an image from javascript to php

Original post
I want to use javascript to send some images to my Server running PHP, which then saves these images. I have followed this guide, but my php script only recieves empty files.
I suspect that this piece of javascript from the guide:
const files = document.querySelector('[type=file]').files;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
let file = files[i];
formData.append('files[]', file);
}
does not really put the whole image data into formData. How can I fix this?
Source code
PHP script:
<?php
echo "Hello world";
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
var_dump($_FILES);
$uploaddir = '/var/www/uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.\n";
} else {
echo "Possible file upload attack!\n";
}
}
?>
Javascript:
const url = "http://localhost:8888/post.php"
const files = document.querySelector('[type=file]').files;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
let file = files[i];
formData.append('files[]', file);
}
console.log(files);
fetch(url, {
method: 'POST',
body: formData
}).then(response => {
response.text().then((text) => {console.log(text)});
});
Output of the javascript:
Without actually seeing all of the code, it's hard to pinpoint any specific issue. If I follow the guide you linked to, I'm able to see the image posted to my server. This is my source code:
process.php:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<?php
var_dump($_POST);
var_dump($_FILES);
?>
<form>
<input type="file">
<input type="submit">
</form>
<script>
const url = 'process.php'
const form = document.querySelector('form')
form.addEventListener('submit', e => {
e.preventDefault()
const files = document.querySelector('[type=file]').files
const formData = new FormData()
for (let i = 0; i < files.length; i++) {
let file = files[i]
formData.append('files[]', file)
}
fetch(url, {
method: 'POST',
body: formData,
}).then(response => {
console.log(response)
})
})
</script>
</body>
</html>
In your case, I would recommend removing the javascript, setting the action attribute of your form to process.php, adding the enctype attribute to the form, setting it to multipart/form-data, and submitting an image that way. If you still don't see any images, then your issue may lie with PHP's configuration.
Your uploaded file may be too large. The default PHP max is 2MB. Try increasing the configuration value for upload_max_filesize. Also, if you increase the value of upload_max_filesize, you'll need to increase post_max_size too.
Here I a link to some common pitfalls: https://www.php.net/manual/en/features.file-upload.common-pitfalls.php

When including session I can not use $_FILES

I am uploading a csv file and sending it to page to process using the js fetch api. I have session included using my init file and all works well accept for the page that should process to file info. Without including the session it works fine and I can process the file, when including it, I can see al the session info, but $_FILES just stops working, I can't see any of the info for some reason.
I really hope this is something stupid
Some code if needed
The init file
<?php
//define Site Root and Includes Path
defined('DS') ? null : define('DS', DIRECTORY_SEPARATOR);
defined('SITE_ROOT') ? null : define('SITE_ROOT', __DIR__ . DS );
defined('INCLUDES_PATH') ? null : define('INCLUDES_PATH', SITE_ROOT);
//get class files
include(INCLUDES_PATH.DS."Session.php");
require_once(INCLUDES_PATH.DS."functions.php");
require_once(INCLUDES_PATH.DS."DB.php");
require_once(INCLUDES_PATH.DS."Validation.php");
require_once(INCLUDES_PATH.DS."User.php");
require_once(INCLUDES_PATH.DS."Swp.php");
require_once(INCLUDES_PATH.DS."Incident.php");
require_once(INCLUDES_PATH.DS."Hira.php");
require_once(INCLUDES_PATH.DS."Mail.php");
The Session.php page
<?php
class Session
{
private $logged_in;
public $user_id;
function __construct()
{
session_start();
$this->check_login();
}
public function is_logged_in() {
return $this->logged_in;
}
private function check_login() {
if (isset($_SESSION['UserID'])) {
$this->user_id = $_SESSION['UserID'];
$this->logged_in = true;
} else {
unset($this->user_id);
$this->logged_in = false;
}
}
}
$session = new Session();
The form page
<?php
//get all the includes
require_once("../php_functions/_init.php");
print_r($_FILES);
//all rows from csv file
$rows = array_map('str_getcsv', file($_FILES['file']['tmp_name'][0]));
//get only the headers
$header = array_shift($rows);
//declare the array
$csv = array();
//create associative array using array_combine
foreach ($rows as $row) {
$csv[] = array_combine($header, $row);
}
print_r($csv);
like I mentioned if I removed the require once from this page it works as expected. Any ideas would help
Just in case you need it here is the js for uploading the file
document.querySelector("#upload_file").addEventListener("click", function () {
const url = 'php/employee/upload_employee_csv_form.php';
const files = document.querySelector('[type=file]').files;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
let file = files[i];
formData.append('file[]', file);
}
console.log(formData);
fetch(url, {
method: 'POST',
body: formData
}).then(response => {
console.log(response);
});
});
I actually figured it out. So my session was being included before I actually added it, to fix this instead of just saying session_start I check to see if the session is there and then only start if necessary.
The code
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
Bonus tip :-)
The above code will work for php 5.4 and up if you are running something lower than 5.4 you can do the following:
if(session_id() == '') {
session_start();
}
Hope this helps

How can JavaScript save to a local file?

There's already a solution for writing file JSON online but I want to save json file locally.
I've tried to use this example http://jsfiddle.net/RZBbY/10/
It creates a link to download the file, using this call
a.attr('href', 'data:application/x-json;base64,' + btoa(t.val())).show();
Is there a way to save the file locally instead of providing a downloadable link?
There are other types of conversion beyond data:application/x-json;base64?
Here's my code:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>jQuery UI Sortable - Default functionality</title>
<link rel="stylesheet" href="http://jqueryui.com/themes/base/jquery.ui.all.css">
<script src="http://jqueryui.com//jquery-1.7.2.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.core.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.widget.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.mouse.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.sortable.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.accordion.js"></script>
<link rel="stylesheet" href="http://jqueryui.com/demos/demos.css">
<meta charset="utf-8">
<style>a { font: 12px Arial; color: #ac9095; }</style>
<script type='text/javascript'>
$(document).ready(function() {
var f = $('form'), a = $('a'),
i = $('input'), t = $('textarea');
$('#salva').click(function() {
var o = {}, v = t.val();
a.hide();//nasconde il contenuto
i.each(function() {
o[this.name] = $(this).val(); });
if (v === '') {
t.val("[\n " + JSON.stringify(o) + " \n]")
}
else {
t.val(v.substr(0, v.length - 3));
t.val(t.val() + ",\n " + JSON.stringify(o) + " \n]")
}
});
});
$('#esporta').bind('click', function() {
a.attr('href', 'data:application/x-json;base64,' + btoa(t.val())).show();
});
</script>
</head>
<body>
<form>
<label>Nome</label> <input type="text" name="nome"><br />
<label>Cognome</label> <input type="text" name="cognome">
<button type="button" id="salva">Salva</button>
</form>
<textarea rows="10" cols="60"></textarea><br />
<button type="button" id="esporta">Esporta dati</button>
Scarica Dati
</body>
</html>
Based on http://html5-demos.appspot.com/static/a.download.html:
var fileContent = "My epic novel that I don't want to lose.";
var bb = new Blob([fileContent ], { type: 'text/plain' });
var a = document.createElement('a');
a.download = 'download.txt';
a.href = window.URL.createObjectURL(bb);
a.click();
Modified the original fiddle: http://jsfiddle.net/9av2mfjx/
You should check the download attribute and the window.URL method because the download attribute doesn't seem to like data URI.
This example by Google is pretty much what you are trying to do.
It is not possible to save file locally without involving the local client (browser machine) as I could be a great threat to client machine. You can use link to download that file. If you want to store something like Json data on local machine you can use LocalStorage provided by the browsers, Web Storage
The possible ways to create and save files in Javascript are:
Use a library called FileSaver
saveAs(new File(["CONTENT"], "demo.txt", {type: "text/plain;charset=utf-8"}));
Create a blob object and offer a “save as”.
var a = document.createElement("a");
a.href = window.URL.createObjectURL(new Blob(["CONTENT"], {type: "text/plain"}));
a.download = "demo.txt";
a.click();
Upload the data, save it on the server.
var data = new FormData();
data.append("upfile", new Blob(["CONTENT"], {type: "text/plain"}));
fetch("SERVER.SCRIPT", { method: "POST", body: data });
Create a writable file stream.
const fileHandle = await window.showSaveFilePicker();
const fileStream = await fileHandle.createWritable();
await fileStream.write(new Blob(["CONTENT"], {type: "text/plain"}));
await fileStream.close();
In NodeJS, simply use the file system module
require("fs").writeFile("demo.txt", "Foo bar!");
<!-- (A) LOAD FILE SAVER -->
<!-- https://cdnjs.com/libraries/FileSaver.js -->
<!-- https://github.com/eligrey/FileSaver.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script>
// (B) "SAVE AS"
var myFile = new File(["CONTENT"], "demo.txt", {type: "text/plain;charset=utf-8"});
saveAs(myFile);
</script>
// (A) CREATE BLOB OBJECT
var myBlob = new Blob(["CONTENT"], {type: "text/plain"});
// (B) CREATE DOWNLOAD LINK
var url = window.URL.createObjectURL(myBlob);
var anchor = document.createElement("a");
anchor.href = url;
anchor.download = "demo.txt";
// (C) "FORCE DOWNLOAD"
// NOTE: MAY NOT ALWAYS WORK DUE TO BROWSER SECURITY
// BETTER TO LET USERS CLICK ON THEIR OWN
anchor.click();
window.URL.revokeObjectURL(url);
document.removeChild(anchor);
<script>
function blobajax () {
// (A) CREATE BLOB OBJECT
var myBlob = new Blob(["CONTENT"], {type: "text/plain"});
// (B) FORM DATA
var data = new FormData();
data.append("upfile", myBlob);
// (C) AJAX UPLOAD TO SERVER
fetch("3b-upload.php", {
method: "POST",
body: data
})
.then((res) => { return res.text(); })
.then((txt) => { console.log(txt); });
}
</script>
<input type="button" value="Go" onclick="blobajax()"/>
<script>
async function saveFile() {
// (A) CREATE BLOB OBJECT
var myBlob = new Blob(["CONTENT"], {type: "text/plain"});
// (B) FILE HANDLER & FILE STREAM
const fileHandle = await window.showSaveFilePicker({
types: [{
description: "Text file",
accept: {"text/plain": [".txt"]}
}]
});
const fileStream = await fileHandle.createWritable();
// (C) WRITE FILE
await fileStream.write(myBlob);
await fileStream.close();
}
</script>
<input type="button" value="Save File" onclick="saveFile()"/>
// (A) LOAD FILE SYSTEM MODULE
// https://nodejs.org/api/fs.html
const fs = require("fs");
// (B) WRITE TO FILE
fs.writeFile("demo.txt", "CONTENT", "utf8", (error, data) => {
console.log("Write complete");
console.log(error);
console.log(data);
});
/* (C) READ FROM FILE
fs.readFile("demo.txt", "utf8", (error, data) => {
console.log("Read complete");
console.log(error);
console.log(data);
});
*/
It all depends on what you are trying to achieve with "saving locally". Do you want to allow the user to download the file? then <a download> is the way to go. Do you want to save it locally, so you can restore your application state? Then you might want to look into the various options of WebStorage. Specifically localStorage or IndexedDB. The FilesystemAPI allows you to create local virtual file systems you can store arbitrary data in.
While most despise Flash, it is a viable option for providing "save" and "save as" functionality in your html/javascript environment.
I've created a widget called "OpenSave" that provides this functionality available here:
http://www.gieson.com/Library/projects/utilities/opensave/
-mike
So, your real question is: "How can JavaScript save to a local file?"
Take a look at http://www.tiddlywiki.com/
They save their HTML page locally after you have "changed" it internally.
[ UPDATE 2016.01.31 ]
TiddlyWiki original version saved directly. It was quite nice, and saved to a configurable backup directory with the timestamp as part of the backup filename.
TiddlyWiki current version just downloads it as any file download. You need to do your own backup management. :(
[ END OF UPDATE
The trick is, you have to open the page as file:// not as http:// to be able to save locally.
The security on your browser will not let you save to _someone_else's_ local system, only to your own, and even then it isn't trivial.
-Jesse
If you are using FireFox you can use the File HandleAPI
https://developer.mozilla.org/en-US/docs/Web/API/File_Handle_API
I had just tested it out and it works!

Categories

Resources