We all know that images are stored in binary. When uploading images, parsing the body is cumbersome without using a third-party library. So let’s take a look at how koA and related libraries can handle uploaded images.
The following code and steps are only for reference. Please visit Github for detailed code
Create a database and create a file table
async createAvatar (filename, mimetype, size, userId) {
const state = `INSERT INTO avatar (filename, mimetype, size, user_id) VALUES (? ,? ,? ,?) `;
const result = await connection.execute(state, [filename, mimetype, size, userId])
return result[0]}Copy the code
The most common way to do some business is to upload user images or some big action pictures. In the case of Nuggets, it’s uploading user profile pictures and post background images.
Handle avatar uploads
For KOA, there is a library called Koa-Multer that makes it easy for us to handle uploaded images. The specific use will not be introduced, please see the previous article. Koa studies. And if we want to do something with an image, we can do it through the JimP library. The volume is small.
First we need to encapsulate an image upload middleware.
const path = require("path")
const multer = require("koa-multer");
const Jimp = require("jimp");
const { AVATAR_PATH, PICTURE_PATH } = require(".. /app/filePath");
// Process the avatar upload
const avatarUpload = multer({
dest: path.resolve(__dirname, AVATAR_PATH)
})
// Handle image uploads
const pictureUpload = multer({
dest: path.resolve(__dirname, PICTURE_PATH)
})
// Process the user avatar
const avatarHandler = avatarUpload.single("avatar")
// Handle the action dynamic avatar
const pictureHandler = pictureUpload.array("picture")
// Handle image size
// The size of the image is determined according to the w* H passed in from the front end. There is no complex processing here, we only do large and medium processing.
const pictureResizeHandler = async (ctx, next) => {
const files = ctx.req.files;
for (let file of files) {
Jimp.read(file.path).then(image= > {
// ADAPTS to the width and height
image.resize(1280, Jimp.AUTO).write(`${file.path}-large`);
image.resize(640, Jimp.AUTO).write(`${file.path}-middle`);
image.resize(320, Jimp.AUTO).write(`${file.path}-small`); })}await next()
}
module.exports = {
avatarHandler,
pictureHandler,
pictureResizeHandler
}
Copy the code
In general, we click the avatars and are accessed through http://localhost/users/avatar/:userId this way. The image is saved in the User table.
// Create an avatar
async createAvatar (ctx, next) {
// Get the uploaded profile picture information.
const { filename, mimetype, size } = ctx.req.file;
const userId = ctx.user.id;
const result = await createAvatar(filename, mimetype, size, userId)
// Save the avatar's URL to the Users table
const avatarUrl = `${APP_HOST}:${APP_PORT}/users/avatar/${userId}`
await saveAvatar(avatarUrl, userId)
ctx.body = result
}
Copy the code
Through the above operation, the picture will be saved to the local specified folder, next, we need to read the avatar information in the database, to return the local avatar to the user.
Let’s read the profile picture first
// Get the profile picture details
async detailAvatar (userId) {
const state = `SELECT * FROM avatar WHERE user_id = ? ; `;
const [result] = await connection.execute(state, [userId])
return result.pop()
}
Copy the code
Now we can read the local file and return the avatar. By user ID
// Return the user profile picture
async getUserAvatar (ctx, next) {
const { userId } = ctx.params;
// Get the details of the current image from the database. It is then used to get the image locally.
const avatarInfo = await detailAvatar(userId);
// 2. Provide image information for browser parsing
ctx.response.set('content-type', avatarInfo.mimetype);
ctx.body = fs.createReadStream(path.resolve(__dirname, `${AVATAR_PATH}/${avatarInfo.filename}`));
}
Copy the code
Let’s take a look at the effect
Handle image upload
In fact, the upload process is the same. However, we also need to deal with the size of the image, which is the above middleware. Create a picture table and get the details of the picture based on the picture name.
// Create a dynamic image
async createPicture (filename, mimetype, size, userId, actionId) {
const state = `INSERT INTO file (filename, mimetype, size, user_id, action_id) VALUES (? ,? ,? ,? ,?) `;
const result = await connection.execute(state, [filename, mimetype, size, userId, actionId])
return result[0]}Copy the code
In general, we click the avatars, is through http://localhost/actions/images/:filename? Type =small. Since images and articles are many-to-one or many-to-many, we need to save the action_id in the File table.
// Create an action image
async createPicture (ctx, next) {
const files = ctx.req.files;
const userId = ctx.user.id;
const actionId = ctx.request.query.actionId;
for (let file of files) {
const { filename, mimetype, size } = file
await createPicture(filename, mimetype, size, userId, actionId)
}
ctx.body = "Upload successful"
}
Copy the code
By doing this, the image will be saved to the local folder (and save three sizes of images). Next, we need to read the corresponding image information in the database, to return the local image.
First to read the profile picture information, according to the picture name (automatically generated name)
// Get image details
async getActionPictureInfoByFileName (filename) {
const state = `SELECT * FROM file WHERE filename = ? ; `;
const [result] = await connection.execute(state, [filename])
return result
}
Copy the code
Now we can read the local file and return the image information. Based on the image name, we can then specify the Type field of query to determine what size image to return.
// Return the action picture
async getActionPictureByFileName (ctx, next) {
// Filename passed through params
const filename = ctx.request.params.filename;
// The type passed in through query
const imageType = ctx.request.query.type;
let imageUrl = ""
const types = ["small"."middle"."large"];
if (types.includes(imageType)) {
imageUrl = path.resolve(__dirname, `${PICTURE_PATH}/${filename}-${imageType}`)}else {
imageUrl = path.resolve(__dirname, `${PICTURE_PATH}/${filename}`)}const result = await getActionPictureInfoByFileName(filename)
ctx.response.set("content-type", result[0].mimetype)
ctx.body = fs.createReadStream(imageUrl)
}
Copy the code
Let’s take a look at the effect
The above code and steps are only for reference. Please visit Github for specific codes