Upload Multiple Files To Github
Hôm nay chúng ta sẽ dùng Github để làm một kho lưu trữ file miễn phí (hình như mỗi tài khoản Github chỉ được 500MB dung lượng miễn phí, dùng hơn phải nâng cấp).
Ở bài viết này thì mình dùng để upload hình lên nhé, mà upload nhiều hình cùng một lúc.
# Bắt đầu.
Chúng ta cần gì ?
- Server (Chắc chắn rồi, chức năng là để nhận dữ liệu rồi gửi lên Github)
- Github Account (Không có thì sao làm được chức năng này)
# Github
Chúng ta cần phải làm một số việc với Github trước.
Truy cập Github rồi tiến hành đăng ký và đăng nhập vào.
Chọn Settings
Kéo xuống phía dưới chọn tiếp Developer settings
Phần này mọi người chọn Personal access tokens
chọn tiếp Tokens (classic)
và chọn dòng chữ Generate a personal access token
Đến trang này mọi người nhập thông tin vào.
note
nhập token này dùng để làm gì.Expiration
chọn option No expiration.Select scopes
chỉ cần chọn option repo là đủ rồi.
Xong rồi thì kéo xuống dưới và chọn nút Generate Token
màu xanh lá thôi.
Xong việc mọi người sẽ nhận được token như trên hình, copy lại và để đâu đó xí chúng ta có việc cần dùng tới.
# Code.
Vào việc, mở Editor lên và code thôi.
Chúng ta sẽ cần tạo 1 project mới
yarn init
và Enter n lần 🤣.
D:\*********\upload-multiple-files-to-github\code\api>yarn init
yarn init v1.22.19
question name (api):
question version (1.0.0):
question description:
question entry point (index.js):
question repository url:
question author:
question license (MIT):
question private:
success Saved package.json
Done in 4.16s.
Giờ thì chúng ta cần install các package sau:
- Express (dùng để tạo http server)
- Multer (dùng để xử lý dữ liệu dạng multipart/form-data từ phía người dùng gửi lên)
- dotenv (Environment dùng để dấu các dữ liệu quan trọng, như là cái token khi nãy chúng ta tạo đó)
- Cors (Giúp không bị chặn Cors ở Server)
yarn add express multer dotenv cors
Chúng ta sẽ cấu trúc thư mục như này.
├── controller
│ └── uploadController.js
│
├── middleware
│ └── handleUpload.js
│
├── server.js
├── .env
├── node_modules
├── package.json
└── package-lock.json
# Bắt đầu ở server.js
require('dotenv').config()
const express = require('express')
const app = express()
const port = 5000
// CORS
const cors = require('cors')
app.use(
cors({
origin: 'http://localhost:3000',
credentials: true,
})
)
app.post('/upload', (req, res) => {
res.status(200).json({ message: 'Ok' })
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Nôn na là tạo một http server với với chỉ một route /upload
với method Post 😸.
Nhớ thêm vào package.json
"scripts": {
"start": "node server.js"
}
Mọi người nhớ config env nữa nhé.
TOKEN_GITHUB_UPLOAD_IMAGES = //copy cái token mọi người tạo ở trên vào đây
GITHUB_REPO = //tên tài khoản/repo mọi người muốn upload lên. Ex: toan/blog
Xong rồi giờ thì.
yarn start
yarn run v1.22.19
$ node server.js
Server đang chạy ở port:5000
Mở Postman lên và test liền thôi.
Ok vậy là server đã hoạt động rồi.
# Middleware
Ở handleUpload.js
chúng ta sẽ có như sau:
// Multer
const multer = require('multer')
const handleUpload = (req, res, next) => {
const maxSize = 1024 * 1024 * 3
const upload = multer({
limits: { fileSize: maxSize },
fileFilter: (req, file, cb) => {
if (file.mimetype !== 'image/png' && file.mimetype !== 'image/jpeg') {
return cb(null, false)
}
cb(null, true)
},
}).array('images', 5)
upload(req, res, function (err) {
if (err) {
return res.status(400).json({ message: 'Có lỗi xảy ra' + err.message })
}
next()
})
}
module.exports = handleUpload
Lại nôn na tiếp là middlware dùng lọc các hình ảnh với kích thước không quá 3Mb
và thuộc các định dạng image/png
và image/jpeg
# Controller.
Ở uploadController.js
chúng ta sẽ có như sau, mình chia từng phần ra nhé.
Khung thì sẽ như thế này.
async function uploadController(req, res) {
try {
//Chúng ta sẽ code ở đây....
}
catch (error) {
return res.status(500).json({ message: 'Có lỗi xảy ra ' + error.message })
}
}
module.exports = uploadController
# Check xem có file hay không
if (!req.files || Object.keys(req.files).length === 0)
return res.status(400).json({ message: 'Vui lòng chọn hình ảnh' })
Rất đơn giản đúng không 😼😼😼.
# Nếu có file thì làm gì ???
Đầu tiên chúng ta sẽ tạo các blob
cho các file
const files = req.files
const blobs = await Promise.all(
files.map((file) => {
const base64 = file.buffer.toString("base64")
const data = {
content: base64,
encoding: "base64"
}
return fetch(`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/blobs`, {
method: "POST",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify(data)
})
})
)
const blobsData = await Promise.all(blobs.map((blob) => blob.json()))
const blobsSHA = blobsData.map((blob) => blob.sha)
Xong rồi thì đến tạo tree
từ blob
đã tạo ở trên.
const treeSHA = await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/trees/main`,
{
method: "GET",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
}
}
)
const treeSHAData = await treeSHA.json()
const treeSHADataSHA = treeSHAData.sha
const treeArray = blobsSHA.map((sha, index) => ({
path: Date.now() + files[index].originalname,
mode: "100644",
type: "blob",
sha
}))
//Đến đây thì mọi người có thể nhận được url và path file ở đây rồi, nhưng đừng hấp tấp nhé, tiếp tục thôi.
const treeData = await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/trees`,
{
method: "POST",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify({
base_tree: treeSHADataSHA,
tree: treeArray
})
}
)
const treeDataData = await treeData.json()
Đã có tree
rồi thì giờ chúng ta tạo commit
như kiểu chúng ta commit code ở Terminal thôi.
const treeDataDataSHA = treeDataData.sha
const commit = await fetch(
`https://api.github.com/repos//${process.env.GITHUB_REPO}/git/commits`,
{
method: "POST",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify({
message: "upload image", //hoặc mọi người có thể chỉnh commit ở đây.
tree: treeDataDataSHA,
parents: [treeSHADataSHA]
})
}
)
const commitData = await commit.json()
Cuối cùng thì chúng ta cần update reference
nữa là xong.
await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/refs/heads/main`,
{
method: "PATCH",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify({
sha: commitDataSHA,
force: true
})
}
)
Khi đã update reference
xong thì chúng ta có thể nhận thông tin của các files ở phần treeArray
.
const imageUrls = treeArray.map((item) => {
return {
public_name: item.path,
url: item.path
}
})
return res.status(200).json({ message: "Upload ảnh thành công", imageUrls })
Cuối cùng thì chúng ta sẽ có uploadController.js
như sau:
async function uploadController(req, res) {
try {
if (!req.files || Object.keys(req.files).length === 0)
return res.status(400).json({ message: "Vui lòng chọn hình ảnh" })
const files = req.files
const blobs = await Promise.all(
files.map((file) => {
const base64 = file.buffer.toString("base64");
const data = {
content: base64,
encoding: "base64"
};
return fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/blobs`,
{
method: "POST",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify(data)
}
);
})
)
const blobsData = await Promise.all(blobs.map((blob) => blob.json()))
const blobsSHA = blobsData.map((blob) => blob.sha)
const treeSHA = await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/trees/main`,
{
method: "GET",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
}
}
)
const treeSHAData = await treeSHA.json()
const treeSHADataSHA = treeSHAData.sha
const treeArray = blobsSHA.map((sha, index) => ({
path: Date.now() + files[index].originalname,
mode: "100644",
type: "blob",
sha
}))
const treeData = await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/trees`,
{
method: "POST",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify({
base_tree: treeSHADataSHA,
tree: treeArray
})
}
)
const treeDataData = await treeData.json()
const treeDataDataSHA = treeDataData.sha
const commit = await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/commits`,
{
method: "POST",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify({
message: "upload image", //hoặc mọi người có thể chỉnh commit ở đây.
tree: treeDataDataSHA,
parents: [treeSHADataSHA]
})
}
)
const commitData = await commit.json()
const commitDataSHA = commitData.sha
// update reference
await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPO}/git/refs/heads/main`,
{
method: "PATCH",
headers: {
Authorization: `Token ${process.env.TOKEN_GITHUB_UPLOAD_IMAGES}`,
"Content-type": "application/vnd.github+json"
},
body: JSON.stringify({
sha: commitDataSHA,
force: true
})
}
)
const imageUrls = treeArray.map((item) => {
return {
public_name: item.path,
url: `https://raw.githubusercontent.com/${process.env.GITHUB_REPO}/main/${item.path}`,
}
})
return res
.status(200)
.json({ message: "Upload ảnh thành công", imageUrls })
} catch (error) {
return res.status(500).json({ message: "Có lỗi xảy ra " + error.message })
}
}
module.exports = uploadController
# Quay trở lại server.js
và sửa lại như sau:
+ const handleUpload = require('./middleware/handleUpload')
+ const uploadController = require('./controller/uploadController')
- app.post('/upload', (req, res) => {
- res.status(200).json({ message: 'Ok' })
- })
+ app.post('/upload', handleUpload, uploadController)
Cuối cùng thì nó sẽ như này:
require("dotenv").config()
const express = require("express")
const app = express()
const port = 5000
// CORS
const cors = require("cors")
app.use(
cors({
origin: "http://localhost:3000",
credentials: true
})
)
const handleUpload = require("./middleware/handleUpload")
const uploadController = require("./controller/uploadController")
app.post("/upload", handleUpload, uploadController)
app.listen(port, () => {
console.log(`Server đang chạy ở port:${port}`)
})
Giờ thì vào test thôi.
Ok hoạt động tốt rồi.
# Kết thúc.
Vậy là chúng ta đã hoàn thành xong chức năng upload hình ảnh lên Github rồi.
Cảm ơn mọi người đã dành thời gian đọc.
Mọi người có thể tham khảo code ở đây