Upload Saved Book Media

Learn how to add the files collection needed to upload book media.

We uploaded the book details in the last lesson. In this lesson, we’ll add the ability to upload media files attached to a book. A book should have a cover page and a digital copy as a pdf. We’ll use the ostrio:files package, which is fantastic for uploading files. We add the package to our project by running meteor add ostrio:files on the terminal.

Files collection

Installing the ostrio:files package adds a FilesCollection. Open the imports/api/files/files.js file. This is the file that handles the file upload from the client and server.

On line 6, a new FilesCollection is created. Some parameters are set, like the allowClientCode which enables the package to allow an upload from the client.

The FilesCollection runs on both the server and client. The onBeforeUpload function runs before the file starts uploading. A check is performed inside the onBeforeUpload function for the type of file we’re uploading and on the size of the file, which, in our example, is less than 10MB. This function returns true if the uploaded file is accepted or false. In this example, a valid file is one with an extension that ends in either pdf, png, jpg, or jpeg and is less than 10MB.

The onAfterUpload function hook is called after the file has been uploaded with the file instance passed as an argument. On lines 24–54, we check the environment we’re running on so that we can perform actions on the uploaded file. We upload two files from the front-end React client—an image file and a pdf file for each book saved.

The image file saves to the database as a base64 string for easy retrieval, while the pdf saves to the file system. In a production application, this will be neither optimal nor advised. A serious application saves files to a third-party storage system like AWS or Goggle Drive and saves the file links to the database.

On line 25, there’s an array of file extensions that we can use to check if one of the uploaded files is an image. If we have an image, we read the image file from the disk because it has already been saved. The fs is a built-in module that refers to the file system in Node. It’s used to read and write data and manipulate the file system. The fs module is imported on line 4.

const fileData = fs.readFileSync(file.path);
const fileToBase64 = fileData.toString("base64");

The fs.readFileSync reads the image file from the disk, which is stored at the file.path location. The fs.readSync() is a synchronous or blocking call. In the case of a large file, the application may become unresponsive. To avoid this, we should use the asynchronous variant of this function. We’re keeping things simple here, though. The fileData is converted to a base64 string by calling toString("base64") on it.

After converting the image to a base64 string, it’s saved to the database. When uploading the image file, we add the bookId as some extra data in the meta tag. This bookId belongs to the book we’re adding files to. We pull the book from the database on line 30 using the bookId as an identifier and update the coverImage key with the base64 string generated from the image. After saving the base64 string, the image file is deleted from the disk by the code on line 41.

If the uploaded file ends with an extension of pdf, we update the bookurl property of the book collection with the _id of the uploaded file.

File uploaded at the client

In a previous lesson, we explained how files are uploaded to the server by coding a small application that shows how it’s done. Open the imports/ui/UploadComponents.jsx file. This file is responsible for uploading the file to the server. The component can process the uploading of multiple files. We upload two files so that the component will process these first two selected files.

On line 4, the component is passed two props from the parent component, which are bookId and closeUploadComponent. The bookId is the _id of the book we’re saving files for, while the closeUploadComponent prop is a function that’s called when the upload is finished.

Open the imports/ui/AddBook.jsx file. The highlighted code is the new code added to the code from the previous lesson. A new book is added from the user interface by calling the books.insertNewBook method on the client. The server responds and returns the _id of the newly-inserted book. Upon the success of the insert operation, the following React state is set:

 setBookDetailsSave(true)
 setBookDetails({
   bookId: bookId,
   title: bookTitle,
});

When the setBookDetailsSave is called, the bookDetailsSave React state is set to true, causing the “save book” button element on line 243 to not be displayed on the page. After a successful save, we’ll want to display the <UploadComponent/> to select an image file and a pdf file and upload it to the server.

On line 249, the UploadComponent is rendered when the saveBooKDetails state is true. We pass the bookId and closeUploadComponent as props to the UploadComponent. The bookId is destructured from the bookDetails state, while the closeUploadComponent passes a function with the same name as the prop to the <UploadComponent/>.

const closeUploadComponent = () => {
 //close the upload component here
    setBookDetails(null);
    setBookDetailsSave(false);
};

The closeUploadComponent is called in line 15 of the UploadComponent after the file uploads. We know that the files are uploaded when the count state of the UploadComponent equals 2 because the count is updated on each file saved. When closeUploadComponet is called from the UploadComponent, the React state of bookDetails and bookDetailsSave are changed in the <AddBook/> component. The <AddBook/> rerenders, and we’ll then be able to add another book to the database collection.

Task

Click “Run,” add a book. Then, upload an image and a pdf file for that book. Don’t forget to log in with the credentials defined in the previous lesson.

Get hands-on with 1200+ tech skills courses.