简体   繁体   中英

Create react app - how to copy pdf.worker.js file from pdfjs-dist/build to your project's output folder?

Since I can't use browser's pdf viewer in the.network where the app is going to be used, I am testing a react-pdf package for loading PDF's with React. I have made a component where I am sending a url of my PDF that I get from backend:

import React, { useState } from 'react';
import { Document, Page } from 'react-pdf';

const PDFViewer = ({url}) => {
  const [numPages, setNumPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);

  function onDocumentLoadSuccess({ numPages }) {
    setNumPages(numPages);
  }
 function onLoadError(error) {
   console.log(error);
 }

 function onSourceError(error) {
   console.log(error);
 }

  return (
    <div>
      <Document
        file={window.location.origin + url}
        onLoadSuccess={onDocumentLoadSuccess}
        onLoadError={onLoadError}
        onSourceError={onSourceError}
      >
        {[...Array(numPages).keys()].map((p) => (
          <Page pageNumber={p + 1} />
        ))}
      </Document>
    </div>
  );
};

export default PDFViewer;

But, on opening the PDFViewer I get an error

Error: Setting up fake worker failed: "Cannot read property 'WorkerMessageHandler' of undefined"

In documentation it says that you should set up service worker and that the recommended way is to do that with CDN:

import { pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

But, I can't use CDN links for my project, and in the documentation it also says:

Create React App uses Webpack under the hood, but instructions for Webpack will not work. Standard instructions apply. Standard (Browserify and others) If you use Browserify or other bundling tools, you will have to make sure on your own that pdf.worker.js file from pdfjs-dist/build is copied to your project's output folder.

There are no instructions on how to do that with create-react-app. How can I set this up locally then?

Install pdfjs-dist

import { Document, Page, pdfjs } from "react-pdf";
import pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

Reference: https://github.com/mozilla/pdf.js/issues/8305

found a more efficient way of including the worker by including the library from the dependencies of react-pdf itself, this way you will never get a version mismatch like this The API version "2.3.45" does not match the Worker version "2.1.266"

if you install pdfjs-dist manually you will have to check react pdf dependency version on every build

import { Document, Page, pdfjs } from "react-pdf";
import pdfjsWorker from "react-pdf/node_modules/pdfjs-dist/build/pdf.worker.entry";

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

see similar error on pdfjs library: https://github.com/mozilla/pdf.js/issues/10997

hope it helps people

You can install worker loader module for webpack:

npm install worker-loader --save-dev

Then use it inline where you are going to work with a worker:

import SomeWorker from 'worker-loader?inline=true!../workers/some.worker'

const someWorker: Worker = new SomeWorker()

someWorker.postMessage(...)

I haven't tried this solution with react-pdf , but it might help.

You may need to add types for TypeScript if you are using it:

declare module 'worker-loader*' {
  class SomeWorker extends Worker {
    constructor()
  }

  export default SomeWorker
}

Just to add that in some .d.ts file in your project.

Install pdfjs-dist then use the webpack module:

import { pdfjs } from 'react-pdf'
import worker from 'pdfjs-dist/webpack'

pdfjs.GlobalWorkerOptions.workerSrc = worker

If your build process uses cli commands, (ie AWS buildspec), you can use this:

mkdir -p build && cp./node_modules/pdfjs-dist/build/pdf.worker.js build

If you are in a corporate codebase environment and have little to no experience configuring WebPack, I wanted to share a little more info if (like me) you struggled with this for quite a long time.

My environment has several complicated WebPack config files (base, production, and development), and the resolution ended up being pretty simple but it escaped me for quite a while because I was unfamiliar with the complicated build process.

1) The Implementation

Quite simple, just as the docs recommend (I went with the minified file). Our React environment required me to use React-PDF@4.2.0, but there aren't any differences here.

import {Document, Page, pdfjs} from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.min.js'

Note: a previous solution recommended grabbing the source from the react-pdf node_modules folder, however, my codebase is setup to install dependencies separately somehow because when I npm install react-pdf, pdfjs-dist is also installed separately. Regardless, this method did not work for my codebase (importing the worker as a variable) due to the way the project is built. The import command acted like it couldn't find the proper named export inside a node_modules folder. It was top-level or nothing.

2) WebPack Config

Since I do not know WebPack at all, but found pretty easily that what I needed to do was take advantage of CopyWebpackPlugin , I searched through those existing dev and prod webpack config files, and found existing copy commands for JQuery and polyfill and added a new plugin to that array:

new CopyWebpackPlugin({from: 'node_modules/pdfjs-dist/build/pdf.worker.min.js})

I had to do this in multiples places in both config files as this large project has several entry point server files for the different services of the website.

3) Inserting Script Tag to HTML Head

This was the crucial part I was missing. There was a "ComponentFactory" file whose job it was to insert chunks of html in the <head> and tail of the html file. I wasn't used to something like this on small projects. So there, I simply copied what was already done for the jquery and polyfill, which included a string literal of the location of the assets folder the webpack was building out to. In my case, that was something like "assets/v1/". So the tag looked like this:

<script src=`${STATIC_ASSETS_URL}/pdf.worker.min.js` defer></script>

It works perfectly, however I am still getting the "Setting Up a Fake Worker" but immediately after that, it loaded it successfully in console and checking the dev tools, it was using the proper file. It's probably just a timing thing of the src set not running high enough in the code, but it was not effecting the outcome, so I let it go.

(Sidebar, if you also get the "TT unknown function" (paraphrasing) error, that can be ignored. It's just a font issue with whatever PDF you are loading and is just a warning, not an error.)

I was facing this issue once I had to use "react-pdf" from within a package. It was solved by importing the worker conditionally into the code:

  1. Conditional import:

 export const getWorker = () => { try { return require('react-pdf/node_modules/pdfjs-dist/legacy/build/pdf.worker.entry.js') } catch () { return require('pdfjs-dist/legacy/build/pdf.worker.entry.js') } }

  1. usage:

 import { Document, Page, pdfjs } from 'react-pdf/dist/umd/entry.webpack' pdfjs.GlobalWorkerOptions.workerSrc = getWorker()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM