Upload xls -> создание из него нового xls -> download xls
Коллеги, добрый день!
Пытаюсь реализовать автоматизировать некий свой процесс в виде приложения django (pet-проект) следующим образом (ожидаемое поведение):
- Методом Drug 'n' Drop перетягиваю в браузер файлик XLS: [
- JavaScript'ом передаю это дело в бэкэнд (это работает, но есть сомнения что корректно):
let uploadButton = document.getElementById("upload-button");
let chosenImage = document.getElementById("chosen-image");
let fileName = document.getElementById("file-name");
let container = document.querySelector(".container");
let error = document.getElementById("error");
let imageDisplay = document.getElementById("image-display");
const fileHandler = (file, name, type) => {
error.innerText = "";
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
//image and file name
let imageContainer = document.createElement("figure");
let img = document.createElement("img");
img.src = reader.result;
//imageContainer.appendChild(img);
imageContainer.innerHTML += `<figcaption>${name}</figcaption>`;
imageDisplay.appendChild(imageContainer);
};
};
//Upload Button
uploadButton.addEventListener("change", () => {
imageDisplay.innerHTML = "";
Array.from(uploadButton.files).forEach((file) => {
fileHandler(file, file.name, file.type);
const data = new FormData();
data.append('file', file);
fetch('', {
method: 'POST',
body: data
})
.then(() => console.log("file uploaded"))
.catch(reason => console.error(reason));
location.reload();
});
});
container.addEventListener(
"dragenter",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
container.addEventListener(
"dragleave",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
},
false
);
container.addEventListener(
"dragover",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
container.addEventListener(
"drop",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
let draggedData = e.dataTransfer;
let files = draggedData.files;
if (files.length != 1){
error.innerText = "Необходимо загрузить ровно один файл. Не более и не менее";
return false;
}
imageDisplay.innerHTML = "";
Array.from(files).forEach((file) => {
fileHandler(file, file.name, file.type);
});
console.info(files)
//document.getElementById("submit").click();
if (files.length == 1) {
console.info("Один файл. Загружаем.");
const data = new FormData();
for (const file of files) {
data.append('file', file);
}
fetch('', {
method: 'POST',
body: data
})
.then(() => console.log("file uploaded"))
.catch(reason => console.error(reason));
location.reload();
}
},
false
);
window.onload = () => {
error.innerText = "";
};
- Далее, этот файлик я сохраняю в каталоге (Это работает)
- Каким-то образом генерирую (на данном этапе не важно как, использую просто готовую заглушку как результат)
- Выгружаю результат клиенту
@csrf_exempt
def start(request):
if request.method == "POST" and request.FILES:
#
# Вот тут файлик сохраняется, обрабатывается,
# Этот кусок кода я убрал, поскольку, даже в таком виде отправка файла пользователю не работает
#
return download_file(request)
#return HttpResponseRedirect("/tfc/")
else:
context = {}
return render(request, 'tfc/index.html', context)
def download_file(request, file_path='tfc/files/report.xlsx'):
# Этот принт использую для того, что бы убедиться что файл доступен
print('Размер', str(os.stat(file_path).st_size))
fp = open(file_path, "rb")
response = HttpResponse(fp.read())
fp.close();
file_type = mimetypes.guess_type(file_path)
if file_type is None:
file_type = 'application/octet-stream'
response['Content-Type'] = file_type
response['Content-Length'] = str(os.stat(file_path).st_size)
response['Content-Disposition'] = "attachment; filename=report.xlsx"
return response
В чем проблема: после перетягивания на dropzone файлика не случается передача файла-заглушки в сторону клиента. При этом я вижу, что вызов и выполнение функции download_file происходит.
Интуиция подсказывает, что я не замечаю какой-то очевидной ошибки, но уже день потратил - не вижу что не так. Помогите, пожалуйста.
Каждый этап по-отдельности работает, а все вместе - нет :)
На всякий случай HTML-ка от экранной формы:
{% block content %}
<link rel="stylesheet" href="/static/tfc/style.css">
<div class="container">
<form method="post" enctype="multipart/form-data">
<input name="file" type="file" id="upload-button" accept="doc/xls" />
</form>
<label for="upload-button">
<i class="fa-solid fa-upload"></i> Перетащите файл сюда
</label>
<div id="error"></div>
<div id="image-display"></div>
</div>
<!-- Script -->
<script type="text/javascript" src="{% static "/tfc/script.js" %} "></script>
</div>
{% endblock %}
Ну, в общем, вдруг кому пригодится.
Сделал следующим образом
- JavaScript'ом передаю это дело в бэкэнд:
let uploadButton = document.getElementById("upload-button");
let chosenImage = document.getElementById("chosen-image");
let fileName = document.getElementById("file-name");
let container = document.querySelector(".container");
let error = document.getElementById("error");
let imageDisplay = document.getElementById("image-display");
//Upload Button
uploadButton.addEventListener("change", () => {
imageDisplay.innerHTML = "";
Array.from(uploadButton.files).forEach((file) => {
const data = new FormData();
data.append('file', file);
console.info($('#myform'));
$('#myform').submit();
});
});
container.addEventListener(
"dragenter",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
container.addEventListener(
"dragleave",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
},
false
);
container.addEventListener(
"dragover",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.add("active");
},
false
);
container.addEventListener(
"drop",
(e) => {
e.preventDefault();
e.stopPropagation();
container.classList.remove("active");
let draggedData = e.dataTransfer;
let files = draggedData.files;
if (files.length != 1){
error.innerText = "Необходимо загрузить ровно один файл. Не более и не менее";
return false;
}
else {
document.querySelector('input[type="file"]').files = files;
$('#myform').submit();
}
},
false
);
window.onload = () => {
error.innerText = "";
};
Далее, этот файлик сохраняю и обрабатываю
Создаю новый файл
Выгружаю результат клиенту
@csrf_exempt
def start(request):
if request.method == "POST" and request.method:
file = request.FILES['file']
fs = FileSystemStorage(location=FILE_TMP_DIR)
filename = fs.save(file.name, file)
#
# Тут совершается магия с обработкой файла filename и на его основе генерируется файл
#
# Совершаю акт отправки файла
response = download_file(request, filename= newfilename)
return response
else:
context = {}
return render(request, 'tfc/index.html', context)
def download_file(request, filename= '/report.xlsx'):
fp = open(FILE_TMP_DIR + filename, "rb")
response = HttpResponse(fp.read())
fp.close();
file_type = mimetypes.guess_type(FILE_TMP_DIR + filename)
if file_type is None:
file_type = 'application/octet-stream'
response['Content-Type'] = file_type
response['Content-Length'] = str(os.stat(FILE_TMP_DIR + filename).st_size)
response['Content-Disposition'] = "attachment; filename=Import.xlsx"
return response
На всякий случай HTML-ка от экранной формы:
{% block content %}
<link rel="stylesheet" href="/static/tfc/style.css">
<div class="container">
<form method="post" enctype="multipart/form-data">
<input name="file" type="file" id="upload-button" accept="doc/xls" />
</form>
<label for="upload-button">
<i class="fa-solid fa-upload"></i> Перетащите файл сюда
</label>
<div id="error"></div>
<div id="image-display"></div>
</div>
<!-- Script -->
<script type="text/javascript" src="{% static "/tfc/script.js" %} "></script>
</div>
{% endblock %}