I have a small JS/HTML front-end where i want to be able to send files to a kotlin backend server. The problem I am having at the moment seems like it is a wierd type like convertsion done by javascript.
note: this is code will not to be actually used but more of a learning experiance with server and file system management. For this reason i also dont want to use something like the FormData() object of javascript though i am fine with using Blob.
the problem lies with the JS part :
const parts = files.map((file) => {
return file.fileData;
});
When i use the code above everything would work fine, but I only send the actual binary of the file not the extra information like size or a separator. When I try to add something like a string or any other object as far as i have tested like so:
const parts = files.map((file) => {
return file.fileData + "---";
});
The code instead of sending the binary, will just send this:
[object File]---[object File]---[object File]---
This is not any type of special syntax that I have used. That is the actual String it sends over (I’ve just copy and pasted).
Below is all the code i am using:
FRONT END
<!DOCTYPE html>
<html>
<head>
<script src="script.js"></script>
<title>File and Folder Upload</title>
</head>
<body>
<form id="uploadForm" enctype="multipart/form-data"></form>
<h1>File and Folder Upload</h1>
<input
type="file"
id="folder"
name="folder[]"
directory=""
webkitdirectory=""
mozdirectory=""
/>
<button onclick="uploadFiles()">Upload</button>
</form>
</body>
</html>
JASCRIPT
function uploadFiles() {
const folderInput = document.getElementById("folder");
var files = [];
for (let i = 0; i < folderInput.files.length; i++) {
const file = folderInput.files[i];
files.push({
length: file.size,
fileName: file.name,
fileData: file,
});
}
const parts = files.map((file) => {
return file.fileData;
});
var blob = new Blob(parts);
fetch("http://localhost:8000/upload", {
method: "POST",
headers: {
"Content-Type": "multipart/form-data",
},
body: blob,
})
.then((response) => {
if (response.ok) {
console.log("Files uploaded successfully");
response.text().then((data) => {
console.log("Return body data: ", data);
});
} else {
console.error("Error uploading files");
}
})
.catch((error) => {
console.error("Error:", error);
});
return false;
}
BACKEND Kotlin
import com.sun.net.httpserver.HttpServer
import com.sun.net.httpserver.HttpHandler
import com.sun.net.httpserver.HttpExchange
import java.io.IOException
import java.io.OutputStream
import java.net.InetSocketAddress
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.FileOutputStream
import java.io.File
class MyServer {
private lateinit var server: HttpServer
fun startServer() {
try {
server = HttpServer.create(InetSocketAddress(8000), 0)
server.createContext("/upload", PutRemoteFiles())
server.executor = null
server.start()
val host = server.address
println("Server started at http://$host")
} catch (e: IOException) {
println("Error starting the server: ${e.message}")
}
}
fun stopServer() {
server.stop(0)
println("Server stopped")
}
private class PutRemoteFiles : HttpHandler {
override fun handle(exchange: HttpExchange) {
// Set CORS headers
val headers = exchange.responseHeaders
headers.add("Access-Control-Allow-Origin", "*")
headers.add("Access-Control-Allow-Methods", "POST")
headers.add("Access-Control-Allow-Headers", "Content-Type")
if (exchange.requestMethod == "POST") {
try {
val requestBody = exchange.requestBody
val reader = BufferedReader(InputStreamReader(requestBody))
val formData = reader.readText()
writeBinFile(formData)
val response = "Received form data: $formData"
exchange.sendResponseHeaders(200, response.length.toLong())
val os: OutputStream = exchange.responseBody
os.write(response.toByteArray())
os.close()
} catch (e: IOException) {
println("Error reading the request body: ${e.message}")
exchange.sendResponseHeaders(500, 0)
exchange.responseBody.close()
}
} else {
val response = "Invalid request"
exchange.sendResponseHeaders(400, response.length.toLong())
val os: OutputStream = exchange.responseBody
os.write(response.toByteArray())
os.close()
}
}
}
}
fun main() {
val server = MyServer()
server.startServer()
println("Server started. Type 'stop' to stop the server.")
var command = readLine()
while (command != "stop") {
command = readLine()
}
server.stopServer()
}
fun writeBinFile(file: String) {
val fileName = "DataBase/output.bin"
val content = file.toByteArray()
try {
val fileOutputStream = FileOutputStream(fileName)
fileOutputStream.write(content)
fileOutputStream.close()
println("File '$fileName' is written successfully.")
} catch (e: IOException) {
println("An error occurred while writing the file: ${e.message}")
}
}
2
Answers
I ended up fixing this by repasenting everything as binary data so that javascript never needs to use type conversions that could never return a
[object Type]
.This was done using the File API and a binary array like so:
if i where you i would have created a general purpose solution that works for more than just your own upload usecase where html defines the logic and the javascript just enhance the experiences.
i would for instance just listen to the onsubmit event rather than on a click handler as that would allow you to use the constraint validation as well.
and then i would also had used the FormData