Vue + Laravel: Как правильно загрузить файл PDF?

СИТУАЦИЯ:

Интерфейс: Vue. Бэкэнд: Laravel.

Внутри веб-приложения мне нужно разрешить пользователю загружать определенные файлы pdf:

  • Мне нужно, чтобы Laravel взял файл и вернул его в качестве ответа на запрос API GET.
  • Затем в моем веб-приложении Vue мне нужно получить файл и загрузить его.

КОД:

API:

$file = public_path() . "/path/test.pdf";

$headers = [
    'Content-Type' => 'application/pdf',
];
return response()->download($file, 'test.pdf', $headers);

Веб-приложение:

downloadFile() {
  this.$http.get(this.apiPath + '/download_pdf')
    .then(response => {
      let blob = new Blob([response.data], { type: 'application/pdf' })
      let link = document.createElement('a')
      link.href = window.URL.createObjectURL(blob)
      link.download = 'test.pdf'
      link.click()
    })
}

ИСХОД:

Используя этот код, мне удалось загрузить файл pdf. Проблема в том, что pdf пустой.

Каким-то образом данные были повреждены (не проблема этого конкретного файла pdf, я пробовал с несколькими файлами pdf - тот же результат)

ОТВЕТ С СЕРВЕРА:

Сам ответ от сервера в порядке:

Vue + Laravel: Как правильно загрузить файл PDF?

PDF:

Проблема может быть в файле pdf. Это определенно выглядит поврежденными данными. Это отрывок того, как выглядит response.data:

Vue + Laravel: Как правильно загрузить файл PDF?

ВОПРОС:

Как я могу правильно загрузить файл pdf, используя Laravel для API и Vue для веб-приложения?

Спасибо!

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
17
0
25 229
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы не сможете выполнить загрузку с Laravel на Vue, поскольку я предполагаю, что оба они работают на разных портах.

Даже если вы попробуете что-то подобное.

public function getDownload()
    {
        //PDF file is stored under project/public/download/info.pdf
        $file= public_path(). "/download/info.pdf";

        $headers = [
              'Content-Type' => 'application/pdf',
           ];

    return response()->download($file, 'filename.pdf', $headers);
    }

Это не поможет, поскольку вы отправляете заголовки в порт Laravel. Попробуйте использовать библиотеки Vue js и попробуйте отправить этот PDF-контент в библиотеку.

Попробуй это Получите помощь отсюда

Выглядит очень похоже. Я попробую использовать Axios вместо vue-resource.

FrancescoMussi 07.06.2018 10:56

Конечно, дайте мне знать, сработало это или нет @johnnyfittizio

Sagar Ahuja 07.06.2018 11:42

В предоставленной вами ссылке я нашел реальное решение. Все было правильно, за исключением того факта, что отсутствовал responseType. Axios или vue-ресурс значения не имеет. Я написал исправленный код в ответ. Я поблагодарил вас за то, что вы указали в правильном направлении. Спасибо!

FrancescoMussi 07.06.2018 11:45
Ответ принят как подходящий

РЕШЕНИЕ:

Код выше был правильным. Чего не хватало, так это добавления правильного responseType как arraybuffer.

Меня напугали те ???? внутри ответа, и это меня вводило в заблуждение. Эти вопросительные знаки были нормальными, поскольку pdf - это двоичные данные, и их должен читать настоящий читатель.

ARRAYBUFFER:

А arraybuffer как раз используется для хранения двоичных данных.

Это определение с веб-сайта Mozilla:

The ArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer. You cannot directly manipulate the contents of an ArrayBuffer; instead, you create one of the typed array objects or a DataView object which represents the buffer in a specific format, and use that to read and write the contents of the buffer.

А строка ResponseType указывает тип ответа. Сообщая ему массив массива, он обрабатывает данные соответствующим образом.

И просто добавив responseType, мне удалось правильно загрузить файл pdf.

КОД:

Это исправленный код Vue (точно так же, как и раньше, но с добавлением responseType):

downloadFile() {
  this.$http.get(this.appApiPath + '/testpdf', {responseType: 'arraybuffer'})
    .then(response => {
      let blob = new Blob([response.data], { type: 'application/pdf' })
      let link = document.createElement('a')
      link.href = window.URL.createObjectURL(blob)
      link.download = 'test.pdf'
      link.click()
    })
}

Обновлено:

Это более полное решение, учитывающее поведение других браузеров:

downloadContract(booking) {
  this.$http.get(this.appApiPath + '/download_contract/' + booking.id, {responseType: 'arraybuffer'})
    .then(response => {
      this.downloadFile(response, 'customFilename')
    }, response => {
      console.warn('error from download_contract')
      console.info(response)
      // Manage errors
      }
    })
},

downloadFile(response, filename) {
  // It is necessary to create a new blob object with mime-type explicitly set
  // otherwise only Chrome works like it should
  var newBlob = new Blob([response.body], {type: 'application/pdf'})

  // IE doesn't allow using a blob object directly as link href
  // instead it is necessary to use msSaveOrOpenBlob
  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(newBlob)
    return
  }

  // For other browsers:
  // Create a link pointing to the ObjectURL containing the blob.
  const data = window.URL.createObjectURL(newBlob)
  var link = document.createElement('a')
  link.href = data
  link.download = filename + '.pdf'
  link.click()
  setTimeout(function () {
    // For Firefox it is necessary to delay revoking the ObjectURL
    window.URL.revokeObjectURL(data)
  }, 100)
},

Большое спасибо @FrancescoMussi! Это очень полезно. Мой PDF-файл загружается, но не открывается или открывается с ошибкой, что его нельзя открыть. Когда я добавил responseType, все заработало!

bdfios 05.07.2019 21:55

responseType как arraybuffer! Работы <3

Frexuz 03.04.2020 05:10

Я уже два дня работаю над потоковой передачей данных в формате PDF, выдергиваю волосы. Я продолжаю получать данные в формате PDF "undefined". Я вижу чертовы PDF-данные в сети, но почему-то не могу их сохранить!

dustbuster 05.08.2020 21:48
downloadFile: function () {
            this.$http.post('{{ route('download.download') }}', {
                _token: "{{ csrf_token() }}",
                inputs: this.inputs
            },{responseType: 'arraybuffer'}).then(response => {
                var filename = response.headers.get('content-disposition').split('=')[1].replace(/^\"+|\"+$/g, '')
                var url = window.URL.createObjectURL(new Blob([response.body],{type:response.headers.get('content-type')}))
                var link = document.createElement('a')
                link.href = url
                link.setAttribute('download', filename)
                document.body.appendChild(link)
                link.click()
            });
        },

Добавьте обсуждение к своему ответу, чтобы объяснить, как он решает вопрос ОП.

quicklikerabbit 22.03.2019 01:18

Для Vue 2.5.22 и Vue-ресурса 1.5.1

Edwin Jaimes 22.03.2019 17:18

это работает для меня.

из бэкэнда laravel:

$pdf = PDF::loadView('your_view_name', ['data' => $data]);
return $pdf->output();

из интерфейса vuejs:

axios({
url: 'http://localhost:8000/api/your-route',
method: 'GET',
responseType: 'blob',
}).then((response) => {
     var fileURL = window.URL.createObjectURL(new Blob([response.data]));
     var fileLink = document.createElement('a');
     fileLink.href = fileURL;
     fileLink.setAttribute('download', 'file.pdf');
     document.body.appendChild(fileLink);
     fileLink.click();

});

Другие вопросы по теме