Отображать уведомление о загрузке, пока Flask готовит send_file - PullRequest
0 голосов
/ 20 апреля 2020

Я обнаружил, что искал этот вопрос, и пришел к решению, которому частично помогли другие потоки stackoverflow.

Контекст:

У меня есть приложение flask, которое обрабатывает большие наборы данных (большая часть этого выполняется через Da sh и Plotly, что я настоятельно рекомендую).

У меня есть одна страница, где пользователь может запросить загрузку определенных частей набора данных. Файлы генерируются динамически и иногда могут быть большими.

Проблема взаимодействия с пользователем:

Когда пользователи нажимают кнопку / ссылку, запрашивающую загрузку файла, страница может сидеть до 10 секунд, пока flask подготавливает динамически сгенерированный файл, а затем в конечном итоге отвечает на запрос send_file и начинается загрузка. Этот период ожидания вводит пользователя в заблуждение, и опыт может помочь вращателю указать, что запрос получен.

Техническая проблема:

Проблема заключается в том, что при указании окно браузера с новым URL-адресом файла, вы теряете контроль над тем, как отображается веб-страница ... другими словами, легко запустить спиннер - остановка - это суть. Эта топика c обсуждалась здесь во многих других потоках, но одно решение выделялось и хорошо интегрировалось с подпрограммой flask send_file. Я попробовал ряд других решений, прежде чем остановиться на том, который я опубликую в разделе «ответы».

Похожие вопросы

Эта реализация может быть полезна для кого-то другого, поскольку она определена c до flask, но вопрос похож на эту тему: Определить, когда браузер получает загрузку файла

Решение, однако, в основном вытекает из этой темы: Загрузка файла jQuery. Ajax

Одним из преимуществ является то, что он сохраняет динамические c соглашения об именах для файла, который вы задаете в flask, добавляя заголовок к ответу. В целом, тем не менее, нет ничего революционного, просто рабочее решение более специфического c вопроса. Но это flask, так что наверняка есть миллион других способов сделать это.

1 Ответ

0 голосов
/ 20 апреля 2020

Как я уже говорил выше, это решение представляет собой комбинацию частей из многих других потоков, но наиболее основательно вдохновлено: Скачать файл jQuery. Ajax

Что он делает

Позволяет пользователю взаимодействовать с динамическими c или stati c ссылками / кнопками загрузки файла и дает обратную связь, когда сервер flask начинает генерировать файл, и обслуживает файл. Другими словами, он отображает счетчик после запуска запроса на файл и скрывает счетчик после начала загрузки. На этом этапе загрузка все еще должна быть запущена, но большинство пользователей ясно знают, как браузер дает отзыв об этом. Пользователь никогда не покидает страницу запроса, что полезно в том случае, если запросы генерируются динамически с помощью форм (формы не меняют состояние).

Сторона flask

@downloads.route('/downloads/report_1/<year>/<month>/<person>')
def report_1(year, month, person):

    #create an output stream
    output = BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter') # for example. another great package

    #prepare your file using arguments

    #the writer has done its job
    writer.close()

    #go back to the beginning of the stream
    output.seek(0)

    response = make_response(send_file(output, attachment_filename=fname, as_attachment=True))
    response.headers['my_filename'] = fname # I had problems with how the native "filename" key
    return response

Javascript

<script type="text/javascript">

        function downloadReport(url) {
          showspinner();

          fetch(url) // for instance: /downloads/report_1/2020/2/Robert
          .then(
            function(response) {
              if (response.status !== 200) {
                console.log('Looks like there was a problem. Status Code: '+response.status);
                return;
              }

              var fname = response.headers.get('my_filename'); // this was necessary because the native filename key was oddly concatinated with another

              // Examine the response
              response.blob().then(function(blob) {
                const url = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.style.display = 'none';
                a.href = url;
                // the filename you want
                a.download = fname;
                document.body.appendChild(a);
                a.click();
                window.URL.revokeObjectURL(url);
                hidespinner();
              });
            }
          )
          .catch(function(err) {
            console.log('Fetch Error :-S', err);
          });

        }

        function showspinner(){
            document.getElementsByClassName("loading_spinner")[0].style.display = "block";
        }

        function hidespinner(){
            document.getElementsByClassName("loading_spinner")[0].style.display = "none";
        }

</script>

Веб-страница

На моей странице есть поля формы, заполненные flask, значения которых используются для генерации url выше, когда кнопка нажата (downloadReport(url) вызывается атрибутом onclick на кнопке).

CSS Spinner

Для полноты, я поделюсь используемым прядильщиком (который можно найти на нескольких сайтах через inte rnet как lds-spinner):

.loading_spinner {
/*   display: inline-block; */
/*   transform: translateZ(1px); */
  position: absolute;
  left:50%;
  top:30%;
  display:none;
}
.loading_spinner > div {
  display: inline-block;
  width: 64px;
  height: 64px;
  margin: 8px;
  border-radius: 50%;
  background: #25aae1;
  animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
@keyframes loading_spinner {
  0%, 100% {
    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
  }
  0% {
    transform: rotateY(0deg);
  }
  50% {
    transform: rotateY(1800deg);
    animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
  }
  100% {
    transform: rotateY(3600deg);
  }
}

со следующим на веб-странице

<div class="loading_spinner"><div></div></div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...