|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<style> |
|
body { |
|
font-family: Arial, sans-serif; |
|
} |
|
input, button, progress { |
|
display: block; |
|
margin-top: 10px; |
|
} |
|
#message { |
|
margin-top: 20px; |
|
color: blue; |
|
} |
|
#error { |
|
color: red; |
|
} |
|
#drop_zone { |
|
width: 300px; |
|
height: 200px; |
|
border: 2px dashed #aaa; |
|
line-height: 200px; |
|
text-align: center; |
|
margin-top: 10px; |
|
} |
|
#file_list { |
|
margin-top: 10px; |
|
} |
|
#fileUpload { |
|
display: none; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<label for="tokenInput">Hugging Face Token:</label> |
|
<input type="password" id="tokenInput" name="tokenInput"> |
|
<label for="repoInput">Repository ID:</label> |
|
<input type="text" id="repoInput" name="repoInput" placeholder="my-user/nlp-model"> |
|
<div id="drop_zone">Drag files/folders here or click to browse from your computer.</div> |
|
<ul id="file_list"></ul> |
|
<input type="file" id="fileUpload" multiple> |
|
<button id="uploadButton">Upload Files</button> |
|
<div id="processingMessage"></div> |
|
<progress id="progressBar" value="0" max="100"></progress> |
|
<div id="message"></div> |
|
<div id="error"></div> |
|
|
|
<script type="module"> |
|
import { createRepo, uploadFiles } from "https://cdn.jsdelivr.net/npm/@huggingface/[email protected]/+esm"; |
|
|
|
class FileUploader { |
|
constructor() { |
|
this.files = []; |
|
this.totalUploaded = 0; |
|
this.startTime = 0; |
|
this.progressMap = new Map(); |
|
|
|
this.fileInput = document.getElementById('fileUpload'); |
|
this.dropZone = document.getElementById('drop_zone'); |
|
this.fileList = document.getElementById('file_list'); |
|
this.uploadButton = document.getElementById('uploadButton'); |
|
this.progressBar = document.getElementById('progressBar'); |
|
this.messageDiv = document.getElementById('message'); |
|
this.errorDiv = document.getElementById('error'); |
|
this.processingMessage = document.getElementById('processingMessage'); |
|
this.tokenInput = document.getElementById('tokenInput'); |
|
this.repoInput = document.getElementById('repoInput'); |
|
|
|
this.hijackFetch(); |
|
|
|
this.fileInput.addEventListener('change', (e) => this.handleFileSelect(e)); |
|
this.dropZone.addEventListener('click', () => this.fileInput.click()); |
|
this.dropZone.addEventListener('dragover', event => event.preventDefault()); |
|
this.dropZone.addEventListener('drop', (e) => this.handleFileDrop(e)); |
|
this.uploadButton.addEventListener('click', () => this.upload()); |
|
} |
|
|
|
hijackFetch() { |
|
const originalFetch = fetch; |
|
|
|
fetch = (url, init) => { |
|
if (init.method !== 'PUT') { |
|
return originalFetch(url, init); |
|
} |
|
|
|
return new Promise((resolve, reject) => { |
|
const xhr = new XMLHttpRequest(); |
|
xhr.open(init.method, url); |
|
|
|
for (let header in init.headers) { |
|
xhr.setRequestHeader(header, init.headers[header]); |
|
} |
|
|
|
this.handleXHR(xhr, resolve, reject, url, init); |
|
}); |
|
}; |
|
} |
|
|
|
handleXHR(xhr, resolve, reject, url, init) { |
|
xhr.onload = () => { |
|
resolve({ |
|
ok: xhr.status >= 200 && xhr.status < 300, |
|
status: xhr.status, |
|
statusText: xhr.statusText, |
|
text: () => Promise.resolve(xhr.responseText), |
|
json: () => Promise.resolve(JSON.parse(xhr.responseText)), |
|
headers: { get: (header) => xhr.getResponseHeader(header) } |
|
}); |
|
}; |
|
|
|
xhr.onerror = () => reject(new TypeError('Network request failed')); |
|
xhr.ontimeout = () => reject(new TypeError('Network request failed due to timeout')); |
|
xhr.upload.onprogress = (event) => this.updateUploadProgress(event, url); |
|
xhr.send(init.body); |
|
} |
|
|
|
updateUploadProgress(event, url) { |
|
if (event.lengthComputable) { |
|
this.progressMap.set(url, event.loaded); |
|
this.totalUploaded = Array.from(this.progressMap.values()).reduce((a, b) => a + b, 0); |
|
|
|
const elapsedSeconds = (Date.now() - this.startTime) / 1000; |
|
|
|
this.progressBar.value = this.totalUploaded; |
|
|
|
if (this.totalUploaded === 0) { |
|
this.processingMessage.textContent = 'Preparing your upload'; |
|
} else if (this.totalUploaded === this.progressBar.max) { |
|
this.processingMessage.textContent = 'Processing your upload'; |
|
} |
|
} |
|
} |
|
|
|
handleFileSelect(event) { |
|
const newFiles = Array.from(event.target.files).map(file => ({ path: file.name, content: file })); |
|
this.files = this.files.concat(newFiles); |
|
this.updateFileList(); |
|
} |
|
|
|
handleFileDrop(event) { |
|
event.preventDefault(); |
|
let items = event.dataTransfer.items; |
|
for (let i = 0; i < items.length; i++) { |
|
let item = items[i].webkitGetAsEntry(); |
|
if (item) { |
|
this.traverseFileTree(item); |
|
} |
|
} |
|
} |
|
|
|
traverseFileTree(item, path = '') { |
|
if (item.isFile) { |
|
item.file((file) => { |
|
this.files.push({ path: path + file.name, content: file }); |
|
this.updateFileList(); |
|
}); |
|
} else if (item.isDirectory) { |
|
let dirReader = item.createReader(); |
|
dirReader.readEntries((entries) => { |
|
for (let i = 0; i < entries.length; i++) { |
|
this.traverseFileTree(entries[i], path + item.name + "/"); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
updateFileList() { |
|
this.fileList.innerHTML = ''; |
|
for (let file of this.files) { |
|
let listItem = document.createElement('li'); |
|
listItem.textContent = file.path; |
|
this.fileList.appendChild(listItem); |
|
} |
|
} |
|
|
|
async upload() { |
|
this.progressBar.value = 0; |
|
this.messageDiv.textContent = ''; |
|
this.errorDiv.textContent = ''; |
|
this.processingMessage.textContent = ''; |
|
const HF_ACCESS_TOKEN = this.tokenInput.value; |
|
const REPO_ID = this.repoInput.value; |
|
|
|
if (this.files.length > 0) { |
|
this.prepareUpload(); |
|
|
|
try { |
|
await this.createRepository(REPO_ID, HF_ACCESS_TOKEN); |
|
await this.uploadFilesToRepo(REPO_ID, HF_ACCESS_TOKEN); |
|
this.handleUploadSuccess(); |
|
} catch (error) { |
|
this.handleErrorDuringUpload(error); |
|
} |
|
} else { |
|
this.messageDiv.textContent = 'No files selected for upload.'; |
|
} |
|
} |
|
|
|
prepareUpload() { |
|
let totalSize = this.files.reduce((total, file) => total + file.content.size, 0); |
|
this.progressBar.max = totalSize; |
|
this.totalUploaded = 0; |
|
this.startTime = Date.now(); |
|
} |
|
|
|
async createRepository(REPO_ID, HF_ACCESS_TOKEN) { |
|
try { |
|
await createRepo({ |
|
repo: REPO_ID, |
|
credentials: { accessToken: HF_ACCESS_TOKEN }, |
|
}); |
|
} catch (error) { |
|
if (error.message !== 'You already created this model repo') { |
|
throw error; |
|
} |
|
} |
|
} |
|
|
|
async uploadFilesToRepo(REPO_ID, HF_ACCESS_TOKEN) { |
|
await uploadFiles({ |
|
repo: REPO_ID, |
|
credentials: { accessToken: HF_ACCESS_TOKEN }, |
|
files: this.files |
|
}); |
|
} |
|
|
|
handleUploadSuccess() { |
|
this.messageDiv.textContent = 'Upload successful!'; |
|
this.processingMessage.textContent = ''; |
|
this.files = []; |
|
this.updateFileList(); |
|
} |
|
|
|
handleErrorDuringUpload(error) { |
|
console.error('Error during upload', error); |
|
this.errorDiv.textContent = 'Error during upload: ' + error.message; |
|
} |
|
} |
|
|
|
new FileUploader(); |
|
</script> |
|
|
|
</body> |
|
</html> |