## Demo: file-upload
### FileUploadDemo.svelte
```svelte
With {'{@attach enhance(...)}'}
Plain HTML
```
### FileUpload.svelte
```svelte
{label}
{#if errorMessage}
{errorMessage}
{/if}
{#if fileResult}
{fileResult.filename} — {fileResult.size} bytes
{fileResult.content}
{/if}
```
### routes.ts
```ts
import { Mochi, fail, success } from 'mochi-framework';
import type { MochiRouteValue } from 'mochi-framework';
export const routes: Record = {
'/demos/file-upload': Mochi.page('./src/demos/file-upload/FileUploadDemo.svelte', {
actions: {
uploadFile: async ({ formData }) => {
const file = formData.get('file');
if (!(file instanceof File) || file.size === 0) {
return fail(400, { error: 'No file selected' });
}
const ext = file.name.split('.').pop()?.toLowerCase() ?? '';
if (ext !== 'txt' && ext !== 'md') {
return fail(400, { error: 'Only .txt and .md files are accepted' });
}
if (file.size > 100 * 1024) {
return fail(400, { error: 'File too large (max 100 KB)' });
}
const content = await file.text();
return success({ filename: file.name, content, size: file.size });
},
},
}),
};
```
### index.ts
```ts
import { Mochi, logger } from 'mochi-framework';
await Mochi.serve({
port: 3333,
development: process.env.MODE === 'development',
routes: {
'/': Mochi.page('./src/Home.svelte'),
},
});
logger.info('Server running at http://localhost:3333');
```