URL.createObjectURL : IE10 & 모든 모던 브라우저
FileReader.readAsDataURL : IE10 & 모든 모던 브라우저
createObjectURL을 사용하는 게 효율적이고 빠르지만, 사용하지 않을 때 일일이 revokeObjectURL로 release시켜주어야 하는 번거로움이 있다.
import React, { useState } from 'react';
import styled from '@emotion/styled';
import { pdfjs } from 'react-pdf';
import 'react-pdf/dist/esm/Page/TextLayer.css';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import PdfViewer from './components/PdfViewer';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
const Container = styled.div`
box-sizing: border-box;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
`;
const ViewerContainer = styled.div`
display: flex;
justify-content: center;
`;
const ButtonContainer = styled.div`
height: 60px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 35px;
`;
const UploadButton = styled.label`
border-radius: 15px;
width: 150px;
height: 60px;
background-color: skyblue;
font-size: 20px;
font-weight: 700;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
&:hover {
opacity: 0.5;
}
`;
function App() {
const [pdfUrl, setPdfUrl] = useState<string | undefined>(
undefined
);
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
if (file.name.split('.').pop()?.toLowerCase() !== 'pdf') {
alert('에러가 발생하였습니다.');
return;
}
const fileReader = new FileReader();
fileReader.onload = () => {
if (typeof fileReader.result === 'string') {
setPdfUrl(fileReader.result);
}
};
fileReader.readAsDataURL(file);
}
};
return (
<Container>
<ViewerContainer>
<PdfViewer fileUrl={pdfUrl} />
</ViewerContainer>
<ButtonContainer>
<UploadButton>
업로드
<input type="file" accept=".pdf" style={{ display: 'none' }} onChange={onFileChange} />
</UploadButton>
</ButtonContainer>
</Container>
);
}
export default App;
import React, { useState } from 'react';
import styled from '@emotion/styled';
import { Document, Page } from 'react-pdf';
const Container = styled.div`
overflow-y: scroll;
height: 600px;
`;
interface IProps {
readonly fileUrl: string | undefined;
}
const PdfViewer: React.FC<IProps> = ({ fileUrl }) => {
const [numPages, setNumPages] = useState<number>(0);
const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
setNumPages(numPages);
};
return (
<Container>
<Document file={fileUrl} onLoadSuccess={onDocumentLoadSuccess}>
{new Array(numPages).fill(null).map((element: null, index: number) => (
<Page pageIndex={index} width={600} key={index} />
))}
</Document>
</Container>
);
};
export default PdfViewer;
import React, { useState } from 'react';
import styled from '@emotion/styled';
import { pdfjs } from 'react-pdf';
import 'react-pdf/dist/esm/Page/TextLayer.css';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import PdfViewer from './components/PdfViewer';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
const Container = styled.div`
box-sizing: border-box;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
`;
const ViewerContainer = styled.div`
display: flex;
justify-content: center;
`;
const ButtonContainer = styled.div`
height: 60px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 35px;
`;
const UploadButton = styled.label`
border-radius: 15px;
width: 150px;
height: 60px;
background-color: skyblue;
font-size: 20px;
font-weight: 700;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
&:hover {
opacity: 0.5;
}
`;
function App() {
const [pdfUrl, setPdfUrl] = useState<string | undefined>(
undefined
);
const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
if (file.name.split('.').pop()?.toLowerCase() !== 'pdf') {
alert('에러가 발생하였습니다.');
return;
}
const blob = new Blob([file]);
const pdfUrl = URL.createObjectURL(blob);
setPdfUrl(pdfUrl);
}
};
return (
<Container>
<ViewerContainer>
<PdfViewer fileUrl={pdfUrl} />
</ViewerContainer>
<ButtonContainer>
<UploadButton>
업로드
<input type="file" accept=".pdf" style={{ display: 'none' }} onChange={onFileChange} />
</UploadButton>
</ButtonContainer>
</Container>
);
}
export default App;
import React, { useState, useEffect } from 'react';
import styled from '@emotion/styled';
import { Document, Page } from 'react-pdf';
const Container = styled.div`
overflow-y: scroll;
height: 600px;
`;
interface IProps {
readonly fileUrl: string | undefined;
}
const PdfViewer: React.FC<IProps> = ({ fileUrl }) => {
const [numPages, setNumPages] = useState<number>(0);
const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
setNumPages(numPages);
};
useEffect(() => {
return () => {
if (fileUrl !== undefined) {
URL.revokeObjectURL(fileUrl);
}
};
}, [fileUrl]);
return (
<Container>
<Document file={fileUrl} onLoadSuccess={onDocumentLoadSuccess}>
{new Array(numPages).fill(null).map((element: null, index: number) => (
<Page pageIndex={index} width={600} key={index} />
))}
</Document>
</Container>
);
};
export default PdfViewer;
언마운트 될 때, revokeObjectURL을 통해 url의 Object를 release해줌