Fix: Adding Support for Uploading Multiple Files (#803)

* added support for uploading multiple files at a time.

* optimized multiple file upload to use a batch upload

* allowing files to upload even if there is one unsupported file
This commit is contained in:
Raghav Tirumale 2024-06-12 06:21:35 -04:00 committed by GitHub
parent 6afbd8032e
commit 673d0d367c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -810,7 +810,7 @@ To get started, just start typing below. You can also type / to see a list of co
if (overlayText == null) { if (overlayText == null) {
dropzone.classList.add('dragover'); dropzone.classList.add('dragover');
var overlayText = document.createElement("div"); var overlayText = document.createElement("div");
overlayText.innerHTML = "Select a file or drag + drop it here to share it with Khoj"; overlayText.innerHTML = "Select file(s) or drag + drop it here to share it with Khoj";
overlayText.className = "dropzone-overlay"; overlayText.className = "dropzone-overlay";
overlayText.id = "dropzone-overlay"; overlayText.id = "dropzone-overlay";
dropzone.appendChild(overlayText); dropzone.appendChild(overlayText);
@ -818,9 +818,10 @@ To get started, just start typing below. You can also type / to see a list of co
const fileInput = document.createElement('input'); const fileInput = document.createElement('input');
fileInput.type = 'file'; fileInput.type = 'file';
fileInput.multiple = true;
fileInput.addEventListener('change', function() { fileInput.addEventListener('change', function() {
const selectedFile = fileInput.files[0]; const selectedFiles = fileInput.files;
uploadDataForIndexing(selectedFile); uploadDataForIndexing(selectedFiles);
}); });
// Remove overlay text after file input is closed // Remove overlay text after file input is closed
@ -844,90 +845,109 @@ To get started, just start typing below. You can also type / to see a list of co
fileInput.click(); fileInput.click();
} }
function uploadDataForIndexing(file) { function uploadDataForIndexing(files) {
var dropzone = document.getElementById('chat-body'); var dropzone = document.getElementById('chat-body');
var badfiles = [];
var goodfiles = [];
var overlayText = document.getElementById("dropzone-overlay");
if (!file || (!allowedExtensions.includes(file.type) && !allowedFileEndings.includes(file.name.split('.').pop()))) { for (let file of files) {
dropzone.classList.remove('dragover'); if (!file || (!allowedExtensions.includes(file.type) && !allowedFileEndings.includes(file.name.split('.').pop()))) {
if (file) { if (file) {
alert("Sorry, that file type is not yet supported"); badfiles.push(file.name);
}
} else {
goodfiles.push(file);
} }
var overlayText = document.getElementById("dropzone-overlay");
if (overlayText != null) {
overlayText.remove();
}
return;
} }
const fileName = file.name; if (badfiles.length > 0) {
var fileContents = null; alert("The following files are not supported yet:\n" + badfiles.join('\n'));
}
var reader = new FileReader();
const formData = new FormData(); const formData = new FormData();
var overlayText = document.getElementById("dropzone-overlay"); var overlayText = document.getElementById("dropzone-overlay");
if (overlayText != null) { if (overlayText != null) {
// Display loading spinner // Display loading spinner
var loadingSpinner = document.createElement("div"); var loadingSpinner = document.createElement("div");
overlayText.innerHTML = "Uploading file for indexing"; overlayText.innerHTML = "Uploading file(s) for indexing";
loadingSpinner.className = "spinner"; loadingSpinner.className = "spinner";
overlayText.appendChild(loadingSpinner); overlayText.appendChild(loadingSpinner);
} }
reader.onload = function (event) { // Create an array of Promises for file reading
fileContents = event.target.result; const fileReadPromises = Array.from(goodfiles).map(file => {
let fileType = file.type; return new Promise((resolve, reject) => {
if (fileType === "") { let reader = new FileReader();
if (fileName.split('.').pop() === "org") { reader.onload = function (event) {
fileType = "text/org"; let fileContents = event.target.result;
} else if (fileName.split('.').pop() === "md") { let fileType = file.type;
fileType = "text/markdown"; let fileName = file.name;
} else if (fileName.split('.').pop() === "txt") { if (fileType === "") {
fileType = "text/plain"; let fileExtension = fileName.split('.').pop();
} else if (fileName.split('.').pop() === "html") { if (fileExtension === "org") {
fileType = "text/html"; fileType = "text/org";
} else if (fileName.split('.').pop() === "pdf") { } else if (fileExtension === "md") {
fileType = "application/pdf"; fileType = "text/markdown";
} } else if (fileExtension === "txt") {
} fileType = "text/plain";
} else if (fileExtension === "html") {
let fileObj = new Blob([fileContents], { type: fileType }); fileType = "text/html";
formData.append("files", fileObj, file.name); } else if (fileExtension === "pdf") {
console.log(formData); fileType = "application/pdf";
} else {
fetch("/api/v1/index/update?force=false&client=web", { // Skip this file if its type is not supported
method: "POST", resolve();
body: formData, return;
}) }
.then((data) => {
console.log(data);
dropzone.classList.remove('dragover');
var overlayText = document.getElementById("dropzone-overlay");
if (overlayText != null) {
overlayText.remove();
} }
// Display indexing success message
flashStatusInChatInput("✅ File indexed successfully"); let fileObj = new Blob([fileContents], { type: fileType });
renderAllFiles(); formData.append("files", fileObj, file.name);
addFileFilterToConversation(fileName); resolve();
loadFileFiltersFromConversation(); };
}) reader.onerror = reject;
.catch((error) => { reader.readAsArrayBuffer(file);
console.log(error); });
dropzone.classList.remove('dragover'); });
var overlayText = document.getElementById("dropzone-overlay");
if (overlayText != null) { // Wait for all files to be read before making the fetch request
overlayText.remove(); Promise.all(fileReadPromises)
} .then(() => {
// Display indexing failure message return fetch("/api/v1/index/update?force=false&client=web", {
flashStatusInChatInput("⛔️ Failed to upload file for indexing"); method: "POST",
body: formData,
}); });
}; })
.then((data) => {
reader.readAsArrayBuffer(file); console.log(data);
dropzone.classList.remove('dragover');
var overlayText = document.getElementById("dropzone-overlay");
if (overlayText != null) {
overlayText.remove();
}
// Display indexing success message
flashStatusInChatInput("✅ File indexed successfully");
renderAllFiles();
for (let file of goodfiles) {
addFileFilterToConversation(file.name);
loadFileFiltersFromConversation();
}
})
.catch((error) => {
console.log(error);
dropzone.classList.remove('dragover');
var overlayText = document.getElementById("dropzone-overlay");
if (overlayText != null) {
overlayText.remove();
}
// Display indexing failure message
flashStatusInChatInput("⛔️ Failed to upload file for indexing");
});
} }
function setupDropZone() { function setupDropZone() {
var dropzone = document.getElementById('chat-body'); var dropzone = document.getElementById('chat-body');