Как реализовать реакцию pdf с функцией печати
Я хотел бы использовать response-pdf для отображения PDF и разработки функции печати для прямой печати (например, с помощью window.print());
Сервер REST разработан с использованием Джерси.
PDF будет сгенерирован с сервера и возвращен клиенту React с использованием Jersey с типом возврата application/pdf. React клиент будет отображать PDF с помощью response-pdf.
Я не хочу объявлять URL-путь в "файле", потому что он снова будет извлекать PDF-файл с сервера, если состояние React изменилось и вызвало повторную визуализацию. Кроме того, мне нужно разработать функцию печати для печати отображаемого PDF (поскольку содержимое PDF может измениться, если снова извлечь PDF с сервера)
Ниже покажу мой код:
Сервер:
@Override
@GET
@Path("/pdf")
@Produces(MediaType.APPLICATION_PDF_VALUE)
public Response testPdf() throws Exception {
File file = new File("C:\\Desktop\\test.pdf");
FileInputStream fileInputStream = new FileInputStream(file);
ResponseBuilder response = Response.ok((Object) fileInputStream);
response.type("application/pdf");
response.header("Content-Disposition", "filename=test.pdf");
return response.build();
}
клиент
import React, { Component } from 'react';
import { Document, Page } from 'react-pdf';
import axios from 'axios';
class MyApp extends Component {
state = {
numPages: null,
pageNumber: 1,
pdfContent: null
}
componentDidMount(){
var that = this;
axio.get("url\Pdf).then((response) => {
that.setState({pdfContent:response.data});
}).catch((error) => {
console.warn(error);
});
}
onDocumentLoadSuccess = ({ numPages }) => {
this.setState({ numPages });
}
printHandler(){
window.print();
}
render() {
const { pageNumber, numPages } = this.state;
return (
<div>
<Document
file={this.state.pdfContent}
onLoadSuccess={this.onDocumentLoadSuccess}
>
<Page pageNumber={pageNumber} />
</Document>
<p>Page {pageNumber} of {numPages}</p>
<button onClick={() => this.setState(prevState => ({
pageNumber: prevState.pageNumber + 1 }))}>Next page</button>
<button onClick={() => this.setState(prevState => ({
pageNumber: prevState.pageNumber - 1 }))}>Prev Page</button>
<button onClick={this.printHandler}/>
</div>
);
} }
Я хочу получить PDF-файл только один раз и отобразить PDF-файл с помощью response-pdf. Также я хочу распечатать отображаемый PDF.
Я попытался преобразовать response.data в base64, следуя этой строке, потому что не удалось: (это приведет к потере содержимого PDF) Кодировать PDF в base64 в ReactJS
Код как:
componentDidMount(){
var that = this;
axio.get("url\Pdf).then((response) => {
let reader = new FileReader();
var file = new Blob([response.data], { type: 'application/pdf' });
reader.onloadend = () => {
that.setState({
base64Pdf:reader.result
});
}
reader.readAsDataURL(file);
}).catch((error) => {
console.warn(error);
});
}
Кто-нибудь может дать мне предложение? Или какой-нибудь лучший способ достичь моей цели?
Спасибо
2 ответа
Обновление при получении сообщения об ошибке от серверной части:
Когда запрос не выполняется, мы получаем объект JSON от серверной части, которая содержит сообщение об ошибке. Проблема заключается в том, что когда мы вынуждаем получить ответ в формате Blob: responseType: 'blob' - независимо от того, не выполнен запрос или нет, мы получаем объект Blob. Итак, я думал об изменении responseType в функции, предоставляемой из axios: transformResponse, но, к сожалению, у нас нет доступа к объекту responseType, только к заголовкам. Здесь: https://github.com/axios/axios/pull/1155 существует открытая проблема о преобразовании в соответствии с responseType, она все еще не решена.
Итак, мой способ решения этой проблемы - использовать fetch вместо axios. Вот пример:
fetch('here.is.your/endpoint', {
method: 'POST', // specifying the method request
body: JSON.stringify(request), // specifying the body
headers: {
"Content-Type": "application/json"
}
}
).then((response) => {
if (response.ok) { // checks if the response is with status 200 (successful)
return response.blob().then(blob => {
const name = 'Report.pdf';
saveAs(blob, name);
});
} else {
return response.json().then((jsonError) => {
this.setState({
modalMessage: jsonError.message // access the error message returned from the back-end
});
});
}
}).catch(function (error) {
this.setState({
modalMessage: "Error in data type received." // general handler
});
});
Надеюсь, это поможет!
Недавно у меня был похожий вариант использования с pdf-частью, мой запрос - Post, но вы можете сделать это Get без проблем. Итак, что происходит:
1) - Я использую axios для отправки запроса на сервер:
2) - запрос - это объект, который я отправляю, но у вас его не будет, поскольку вы, вероятно, будете отправлять только идентификатор, например: axios.get ('here.is.your / endpoint / id');
3) - Я использую: File-Saver для сохранения файла, который я получаю.
Остальная часть кода должна быть понятна, и я также добавил несколько комментариев.
import {saveAs} from "file-saver";
...
axios.post('here.is.your/endpoint', qs.parse(request), {
headers: {
'Content-Type': 'application/json'
},
responseType: 'blob' // here I am forcing to receive data in a Blob Format
})
.then(response => {
if (response.data) {
//Create a Blob from the PDF Stream
const file = new Blob(
[response.data],
{type: 'application/pdf'});
const name = 'Report.pdf';
saveAs(file, name);
} else {
throw new Error("Error in data type received.");
}
})
.catch(error => {
this.setState({
modalMessage: "Here Add Custom Message"
});
});
Я не могу получить сообщение об ошибке от серверной части, я отправлю текстовое сообщение, если получу какой-то прогресс - пока я показываю пользовательское сообщение.
Надеюсь, это поможет!
Удачи тебе!
Я рад, что помог!
У меня есть еще одно ОБНОВЛЕНИЕ для получения сообщения об ошибке
ЭТО ОДИН ДЕЙСТВИТЕЛЬНО ТОЛЬКО ЕСЛИ ВЫ ПОЛУЧАЕТЕ ТЕКСТОВОЕ СООБЩЕНИЕ, НЕ JSON
fetch('here.is.your/endpoint', {
method: 'POST', // specifying the method request
body: JSON.stringify(request), // specifying the body
headers: {
"Content-Type": "application/json"
}
}
).then((response) => {
if (response.ok) { // checks if the response is with status 200 (successful)
return response.blob().then(blob => {
const name = 'Report.pdf';
saveAs(blob, name);
});
} else {
return response.text().then(function (error) {
throw new Error(error); // we should throw an Error with the received error
}
);
}
}).catch(function (error) {
this.setState({
modalMessage: error.message // that way we access the error message
});
});
Мы используем response.text(). Then(), потому что таким образом нам удается преобразовать его из Promise в текст. И важно использовать.then(), потому что Обещание в этот момент разрешено, и мы получаем значение Обещания. Затем мы просто выдаем ошибку, потому что у нас нет доступа к объекту состояния.
Вот как вы получаете текст из ответа.