Upload Saved Book Media
Learn how to add the files collection needed to upload book media.
We'll cover the following
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 1400+ tech skills courses.