# Handling Form Submission and Sending Files to SvelteKit Endpoints

In the [last blog post](https://jeffpohlmeyer.com/google-drive-folder-setup-and-sveltekit-uploadrename-endpoints) I touched on setting up a Google Drive folder to serve as the location of our uploads as well as creating the necessary endpoints for uploading and renaming the files.

In this post I'll set up functions to send any necessary files to these endpoints to upload. Then in the last post I'll add the markup to handle file acquisition.
# SvelteKit Component Script
The functionality for this script is fairly complex, so most of this post will handle this logic.
## Variable Instantiation
The only thing we'll really need at this point in time is an object to hold accepted and rejected files. I'll be using [Svelte File Dropzone](https://www.npmjs.com/package/svelte-file-dropzone) for file acquisition and the setup used in the docs is as follows
```
<script>
  let files = {
    accepted: [],
    rejected: []
  }
</script>
```
That's it. From the perspective of basic functionality this is all we'll need. When I add in Dropzone-specific stuff I'll add more variable instantiation, but I'll leave that for the next post.
## Handling Form Submission
While I was working on this for my client, this was the feature on which I spent the most time. I'm not terribly familiar with handling readable streams, transmitting them to an endpoint as the correct type, etc. On top of that, I was trying to make the submit button disabled and add a loading spinner and then once all of the files were uploaded I would then add a notification and stop the loading spinner. This would have been less of an issue with a single file, but I wanted to allow for multiple file submissions at the same time. I found a [great Stack Overflow answer](https://stackoverflow.com/a/67484772/1016708) about how to handle this, and it effectively wraps the `reader.onload` functionality in a Promise and you can just await a `Promise.all` for all of the necessary files.
### File Reader
This will be broken up into two smaller sections:
- The file reader
- The rest of `handleSubmit`

Following along a bit of the aforementioned Stack Overflow answer, I'm going to share the code in its entirety and discuss after
```javascript
const filePromises = files.accepted.map((file) => {
	return new Promise((resolve, reject) => {
		const reader = new FileReader();
		reader.onloadend = async () => {
			try {
				const response = await fetch('/upload-files', {
					method: 'POST',
					body: reader.result,
					headers: { 'Content-Type': 'application/pdf' }
				});
				const { id } = await response.json();
				const body = JSON.stringify({ name: file.name, fileId: id });
				await fetch('/rename-file', {
					method: 'POST',
					body
				});
				resolve();
			} catch (err) {
				reject(err);
			}
		};
		reader.onerror = (error) => reject(error);
		reader.readAsArrayBuffer(file);
	});
});
```
The general flow of this method is as follows (for each file in the `files.accepted` array)
- Create a new `FileReader`
- When the file is fully loaded, try sending the `reader.result` element to the previously-created endpoint with the correct `Content-Type` header
- Pull the `id` value from the endpoint that we'll use to rename the file
- Create and stringify the submission body to rename the file
- Send the data to rename the file using that previously-created endpoint

### Handle Submission
I'm going to add more functionality than is necessary to the `handleSubmit` method and will explain why after
```javascript
const handleSubmit = async () => {
	loading = true;
	const filePromises = files.accepted.map((file) => {
		return new Promise((resolve, reject) => {
			const reader = new FileReader();
			reader.onloadend = async () => {
				try {
					const response = await fetch('/upload-files', {
						method: 'POST',
						body: reader.result,
						headers: { 'Content-Type': 'application/pdf' }
					});
					const { id } = await response.json();
					const body = JSON.stringify({ name: file.name, fileId: id });
					await fetch('/api/rename-file', {
						method: 'POST',
						body
					});
					resolve();
				} catch (err) {
					reject(err);
				}
			};
			reader.onerror = (error) => reject(error);
			reader.readAsArrayBuffer(file);
		});
	});
	try {
		await Promise.all(filePromises);
		files.accepted = [];

        /* Extra functionality for fun */
		notification = true;
		setTimeout(() => {
			notification = false;
		}, 10000);
		fetch('/api/send-uploaded-message', {
			method: 'POST',
			body: JSON.stringify({ messageType: 'success' })
		});
        /***************************/
	} catch (err) {
		console.log('err', err);
	} finally {
		loading = false;
	}
};
```
I've added in a bit of extra functionality for demonstration purposes.
- If we want to have a loading spinner that is displayed based on a variable called something like `loading` then we can instantiate it when the function is triggered and then stop it in the `finally` block of the `try/catch/finally` group
- If we want to include a notification on the page then we can do that after the `Promise.all` evaluation
  - I set up a `setTimeout` to close the notification automatically after 10 seconds
- We can also have a separate endpoint to notify whomever it's worth notifying upon successful completion of the file upload, which I've added as well

# Summary
These last two posts have been a bit shorter, but I wanted to keep them to the point and not clutter with a lot of information. In the next (last) blog post I'll add functionality to handle for file management within the component itself.
