Web upload / download allows end users to upload and download files directly through a web browser. External users without an installed agent can also transfer large files, while the web control handles chunked transfers, resume, and progress display.
There are three components.
| Component | Role |
|---|---|
exabyter.js · exabyter.css | Transfer control embedded in the browser. Handles the file selection UI, chunked transfers, progress, and resume |
upload.jsp / download.jsp | Server receive/send endpoints. This is where the actual storage path is configured |
WEB-INF/lib/InnorixJAVA.jar | Transfer library used by the endpoints |
⚠️ Review the security requirements before deployment. The server endpoints accept values sent by the browser as-is. In particular, downloads append
fileNameto the file path, so without path validation, files outside the storage root may be exposed. Read the Required security measures section below first.
ℹ️ Code tabs — Because this configuration consists of a browser script + server endpoint, the examples are provided in two tabs:
JavaScript(client) andJSP(server). The bundle generated by the builder also consists of these two file types.
Getting started
What the builder generates
Choose options in the Web upload / download tab of the builder and download them to generate a zip with the following structure. Extract it directly under webapps/ in Tomcat (or another servlet container) and it will run immediately.
exabyter-web-upload/
upload.html <- page built from the options you picked + exabyter.create({...})
upload.jsp <- receiving endpoint (set the storage path here)
exabyter.js
exabyter.css
img/ font/ <- assets referenced by the css
WEB-INF/lib/InnorixJAVA.jar
README.txt
If you choose download mode, the same structure includes download.html · download.jsp.
After deployment, the main setting you need to change is the endpoint's storage path.
upload.jsp—directory(upload storage root),maxPostSize(maximum request body size)download.jsp—filePath(download source root)
Required security measures
The bundle generated by the builder is a minimal configuration for verifying functionality. Before exposing it to external users, you must apply the following four measures.
| Item | If left unchanged | Required action |
|---|---|---|
| Endpoint authentication | upload.jsp · download.jsp have no authentication, so anyone who knows the URL can call them | Check the session/token at the beginning of the page and return 401 on failure |
| Download path validation | fileName becomes part of the file path directly, so ../ could read files outside the storage root | Verify that the canonical path remains inside the storage root. More securely, perform file ID → actual path mapping on the server and do not accept the path itself |
| Upload storage path | The default is a data folder next to the JSP, which may be exposed through the web | Change it to an absolute path outside the web-accessible area |
| CORS | Reflecting the request Origin directly allows arbitrary sites to call the endpoint | Restrict it to an allowlist of domains |
The implementation examples below already include authentication checks, path validation, and domain restrictions. Change only the values to match your service.
Implementation
Upload
The control is rendered inside a single empty <div>.
<link rel="stylesheet" type="text/css" href="./exabyter.css">
<script src="./exabyter.js"></script>
<div id="fileControl"></div>
<div class="toolbar">
<button onclick="control.openFileDialog()">Attach File</button>
<button onclick="control.directoryDialog()">Attach Folder</button>
<button onclick="control.upload()">Start Upload</button>
</div>
// upload.html - same shape as the bootstrap code the builder generates.
var control = exabyter.create({
setElementID: '#fileControl',
setViewType: 'ListView',
transferMode: 'upload',
setUploadURL: './upload.jsp', // receiving endpoint
setTransferStart: { upload: 'manual' }, // 'auto' = start as soon as files are selected
// How files are attached - the builder's Method
enableDropZone: true, // Drag & drop
addFolder: true, // allow folder attachment (Files and folders)
// File policy - the builder's File & storage rules
allowType: ['pdf', 'docx', 'xlsx', 'png'], // omit to allow every type
maxFileCount: 50,
maxFileSize: 1073741824, // 1 GB (bytes)
maxTotalSize: 10737418240, // 10 GB (bytes)
resumeType: 'numbering', // duplicate name: overwrite | numbering | relay (resume)
// Appearance
showTransferWindow: true,
showTransferStatusIcon: true,
enableComplexFolders: false, // hide the folder tree on the left (flat list)
hideClientPath: true,
transferWindowTitle: 'Exabyter',
controlLang: 'ko', // control UI language
});
// Control is ready
control.on('loadComplete', function () {
setStatus('Ready.', 'ready');
});
// All files attached - call upload() here to start automatically
control.on('afterAddFiles', function () {
// control.upload();
});
control.on('uploadStart', function () {
// Upload started
});
control.on('uploadProgress', function () {
// Progress changed
});
control.on('uploadComplete', function () {
setStatus('Upload complete.', 'done');
// Call your service API on completion (the builder's Call API)
fetch('/your/api', { method: 'POST', credentials: 'include' });
});
control.on('uploadError', function () {
setStatus('Upload error - see the console for details.');
});
control.on('uploadCancel', function () {
// Canceled by the user
});Download
The key to downloads is passing the list of files to offer to the control. Items passed to setDownloadList() become the on-screen list, and only the items selected by the user are downloaded.
ℹ️ The JSP below contains the core logic for authentication checks, path validation, and integrity responses. The complete file, including byte-range calculations and streaming, is included in the bundle's
download.jsp(downloaded through Get API Code). This code block alone does not constitute a complete download server.
// download.html
var control = exabyter.create({
setElementID: '#fileControl',
setViewType: 'ListView',
transferMode: 'download',
setTransferStart: { download: 'manual' }, // 'auto' = start as soon as the list is set
downloadDuplicate: 'numbering', // duplicate name: overwrite | numbering | resume
downloadIntegrity: true, // verify file integrity
setVerification: 'enable',
showTransferWindow: true,
showTransferStatusIcon: true,
enableComplexFolders: false,
hideClientPath: true,
transferWindowTitle: 'Exabyter',
controlLang: 'ko',
});
control.on('loadComplete', function () {
setStatus('Ready.', 'ready');
});
control.on('downloadStart', function () { /* transfer started */ });
control.on('downloadProgress', function () { /* progress changed */ });
control.on('downloadComplete', function () {
setStatus('Download complete.', 'done');
});
control.on('downloadError', function () {
setStatus('Download error - see the console for details.');
});
control.on('downloadCancel', function () { /* canceled by the user */ });
// Files to offer - usually mapped straight from your service API response.
control.setDownloadList([
{
downloadURL: './download.jsp?fileName=report_2026Q3.pdf',
displayFileName: 'report_2026Q3.pdf', // name shown to the user
fileSize: 15728640, // bytes
isFolder: false,
},
{
downloadURL: './download.jsp?fileName=training_video.mp4',
displayFileName: 'training_video.mp4',
fileSize: 5368709120,
isFolder: false,
},
]);
// Wire up your buttons
// control.downloadSelectedFiles(); // selected items only
// control.download(); // everythingServer settings you must update
| Location | Value | Description |
|---|---|---|
upload.jsp | directory | Upload storage root. The default is the data folder next to the JSP, so change it to the actual storage path. |
upload.jsp | maxPostSize | Maximum request body size (bytes). This must be aligned with limits on the front-end proxy (nginx, etc.). |
upload.jsp | CORS | The default code reflects the request Origin directly. In production, restrict it to allowed domains. |
download.jsp | filePath | Download source root. |
| Common | Authentication · path validation · CORS | Before deployment, confirm again that the four Required security measures above have been applied. |
To separate folders by user/transfer, configure the upload endpoint as follows.
// Per transfer
uploader.setDirectory(directory + "/" + uploader.getParameter("_transferId"));
// Per user (take the user id from the session)
String userId = (String) session.getAttribute("userId");
uploader.setDirectory(directory + "/" + userId);
// Per user and per transfer
uploader.setDirectory(directory + "/" + userId + "/" + uploader.getParameter("_transferId"));
Reference
Builder options ↔ generated code mapping
Upload tab
| Builder item | Option | Where it is applied |
|---|---|---|
| Method | Drag & drop | enableDropZone: true |
| File select button | enableDropZone: false + attach button | |
| Connect to existing UI | Connect control.upload() directly to the existing UI without a button | |
| Start method | Click the upload button | setTransferStart: { upload: 'manual' } |
| Auto-start after selecting files | Call control.upload() in afterAddFiles | |
| Selection target | Single file | maxFileCount: 1 |
| Files and folders | addFolder: true + attach folder button | |
| File type | Images / Documents / Videos / Custom | allowType: [...] |
| Max file count | 1 / 10 / 50 / 100 / Custom | maxFileCount |
| Max file size | 100MB / 1GB / 5GB / 10GB / Custom | maxFileSize (bytes) |
| Max total size | 1GB / 5GB / 10GB / 50GB / Custom | maxTotalSize (bytes) |
| Duplicate file name | Overwrite | resumeType: 'overwrite' |
| Save with a new name / Don't overwrite | resumeType: 'numbering' | |
| Storage method | Fixed folder | Use directory in upload.jsp as-is |
| Per-user / Per-upload / Per-user & per-upload | Branch with uploader.setDirectory(...) in upload.jsp | |
| Completion message | Enter message | Status message in the uploadComplete handler |
| Call API | URL + POST/GET | fetch(url, { method }) in uploadComplete |
| Developer event hooks | Selected items | control.on('uploadStart' | 'uploadProgress' | 'uploadComplete' | 'uploadError' | 'uploadCancel', …) |
Download tab
| Builder item | Where it is applied |
|---|---|
| Files to offer | control.setDownloadList([{ downloadURL, displayFileName, fileSize, isFolder }]) |
| Start method | setTransferStart: { download: 'manual' | 'auto' } |
| Duplicate name | downloadDuplicate: 'overwrite' | 'numbering' | 'resume' |
| Integrity | downloadIntegrity: true + setVerification: 'enable' |
| Event hooks | control.on('downloadStart' | 'downloadProgress' | 'downloadComplete' | 'downloadError' | 'downloadCancel', …) |
ℹ️ Items such as screen configuration (displayed file-list fields), file naming rules, access permissions, and availability period are determined by your service when building the list. Pass only the final list to the control. Files are transferred one by one exactly as listed.
Common errors
| Symptom | Cause and solution |
|---|---|
| Control does not appear | The <div> referenced by setElementID does not exist when the script runs, or create() was called before exabyter.js loaded. |
| Upload fails immediately after starting | The setUploadURL path is incorrect or upload.jsp has not been deployed. Check for a 404 in the browser Network tab first. |
| CORS error | The control sends OPTIONS before POST. The Access-Control-Allow-* headers must also be included in the OPTIONS response. |
| 413 on large files | Increase both maxPostSize and the request body size limit on the front-end proxy. |
| Files are saved to the wrong location | The default directory is the data folder next to the JSP. Change it to an absolute path. |
| Korean filenames are corrupted | Check the JSP's pageEncoding="UTF-8" and the container's URI encoding setting (URIEncoding=UTF-8). |
| Extension restriction is not applied | allowType is a lowercase array without dots (['pdf','png']). Because this is client-side validation, validate it again on the server. |
| Download list is empty | Confirm that setDownloadList() is called after loadComplete and that each item has displayFileName. |