Upload xls -> создание из него нового xls -> download xls

Коллеги, добрый день!

Пытаюсь реализовать автоматизировать некий свой процесс в виде приложения django (pet-проект) следующим образом (ожидаемое поведение):

  1. Методом Drug 'n' Drop перетягиваю в браузер файлик XLS: [введите сюда описание изображения
  2. 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 = "";
};
  1. Далее, этот файлик я сохраняю в каталоге (Это работает)
  2. Каким-то образом генерирую (на данном этапе не важно как, использую просто готовую заглушку как результат)
  3. Выгружаю результат клиенту
@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>&nbsp; Перетащите файл сюда
        </label>
        <div id="error"></div>
        <div id="image-display"></div>
    </div>

    <!-- Script -->
    <script type="text/javascript" src="{% static "/tfc/script.js" %} "></script>
    </div>


{% endblock %}

Ну, в общем, вдруг кому пригодится.

Сделал следующим образом

  1. 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 = "";
};
  1. Далее, этот файлик сохраняю и обрабатываю

  2. Создаю новый файл

  3. Выгружаю результат клиенту

@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>&nbsp; Перетащите файл сюда
        </label>
        <div id="error"></div>
        <div id="image-display"></div>
    </div>

    <!-- Script -->
    <script type="text/javascript" src="{% static "/tfc/script.js" %} "></script>
    </div>


{% endblock %}
Вернуться на верх