1. 初期化の準備
1.1 インストールの依存関係
まずexpress-multer-upload
エンジニアリング プロジェクトを作成し、次にプロジェクト内のさまざまな依存パッケージをダウンロードします。
multerミドルウェア
Multerは、multipart/form-data型のフォームデータを処理するためのnode.jsミドルウェアで、主にファイルのアップロードに使用されます。
注: Multer は、非 multipart/form-data タイプのフォーム データを処理しません。
以下は私がダウンロードした依存関係とバージョンです。
1.2 プロジェクト構成区分
このプロジェクトをより標準的な形で完成させるためには、合理的なプロジェクト構造分割を行う必要があります。次のように:
2.multerアップロードロジック
2.1 マルチター設定
multer ディレクトリに multerConfig.js を作成し、次のコードを記述します。
- 依存関係を導入する
- 処理パス関数をカプセル化します。
- マルチターの構成オブジェクトを設定する
- マルチターの設定を追加する
// 1. 引入依赖
const multer = require('multer')
const path = require('path')
// 2. 封装处理路径函数
const handlePath = (dir) => {
return path.join(__dirname, './', dir)
}
// 3. 设置 multer 的配置对象
const storage = multer.diskStorage({
// 3.1 存储路径
destination: function(req, file, cb) {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png' || file.mimetype==='image/gif') {
cb(null, handlePath('../../public'))
} else {
cb({
error: '仅支持 jpg/png/gif 格式的图片!' })
}
},
// 3.2 存储名称
filename: function (req, file, cb) {
// 将图片名称分割伪数组,用于截取图片的后缀
const fileFormat = file.originalname.split('.')
// 自定义图片名称
cb(null, Date.now() + '.' + fileFormat[fileFormat.length - 1])
}
})
// 4. 为 multer 添加配置
const multerConfig = multer({
storage: storage,
limits: {
fileSize: 2097152 } // 2M
})
module.exports = multerConfig
在该配置中可以设置文件保存的地址、文件名称、限制上传的文件格式、文件大小
2.2 upload上传逻辑
在 multer 目录下创建 upload.js,编写如下代码:
// 引入配置好的 multerConfig
const multerConfig = require('./multerConfig')
// 上传到服务器地址
const BaseURL = 'http://localhost:3001'
// 上传到服务器的目录
const imgPath = '/public/'
// 封装上传图片的接口
function uploadAvatar(req, res) {
return new Promise((resolve, reject) => {
multerConfig.single('file')(req, res, function (err) {
if (err) {
// 传递的图片格式错误或者超出文件限制大小,就会reject出去
reject(err)
} else {
// 拼接成完整的服务器静态资源图片路径
resolve(BaseURL + imgPath + req.file.filename)
}
})
})
}
module.exports = uploadAvatar
上記のコードは主にファイルをアップロードするためのメソッドをカプセル化しており、画像のアップロードが成功すると、結合された画像のリンクが解決されます。このメソッドはコントローラーで呼び出されます。
注: 上記の multerConfig.single('file') は単一ファイルのアップロードを示し、フィールド名は「file」です。後で画像をアップロードするためのフィールドは一貫している必要があります。
3. コントローラーを作成し、ルートを定義します
3.1 コントローラーを作成する
controllers
ディレクトリの下に作成しUserController.js
、次のコードを記述します。
const uploadAvatar = require('../multer/upload')
// 用户的逻辑控制器
const UserController = {
// 头像图片上传
async upload(req, res) {
try {
const uploadRes = await uploadAvatar(req, res)
res.send({
meta: {
code: 200, msg: '上传成功!' },
data: {
img_url: uploadRes}
})
} catch (error) {
res.send(error)
}
}
}
module.exports = UserController
上記のコードは主に、ユーザー コントローラー クラスUserController
と写真をアップロードするためのメソッドを記述しますupload
。upload
で写真をアップロードするためのインターフェースが呼び出され、uploadAvatar
成功または失敗の結果が取得され、応答がクライアントに送信されます。
3.2 ルートの定義
① router ディレクトリにindex.jsを作成し、以下のコードを記述します。
const express = require('express')
// 导入用户逻辑
const userController = require('../controllers/UserController')
// 创建路由对象
const router = express.Router()
// 设置路由
router.post('/upload/avatar', userController.upload)
// 导入路由对象
module.exports = router
② ルートを定義したら、app.js にルートを登録し、次のコードを追加する必要があります。
// 导入定义的路由
const router = require('./src/routers/index')
// 注册路由
app.use('/user', router)
在 app.js 中,增加上面两行代码即可完成路由注册
4.写真をアップロードする
次に、テスト リンクを入力し、ポストマン ツールを使用してテストします
。応答データが正常に取得されたことがわかります。このデータには、画像のリンク アドレスも含まれています
。
- フォームはフォームデータ形式である必要があります
- ファイルのフィールドはバックエンドと一致している必要があります
5. 画像名の最適化
これはユーザーがアバター写真をアップロードする機能であるため、2回目にアバターをアップロードする際には元の写真を削除する必要があり、そうしないと常に古い写真がサーバーに保存されてしまいます。当初のアイデアは、ユーザー ID を画像名として使用し、画像がアップロードされるたびに元の画像が上書きされるようにすることでした。しかし、これには 2 つの問題があります。
- 異なる形式の画像 (jpg、png、gif) は上書きされずに残ります。
- 上書きできる場合、画像リンク アドレスは変更されず、データベースに保存されるときの最後の画像アドレスと同じになります。これにより、フロントエンド ページが変更に応じて変更されなくなります。静的リソース内のアバター画像
したがって、ここで使用される方法は、最初に画像の名前を結合して最適化し、次の形式に変更することです: ;时间戳.用户id.jpg
これにより、各画像が繰り返されないことが保証されるだけでなく、ユーザーの ID も含まれます。
注: タイムスタンプは、一意性を確保するために md5 を使用して暗号化できます。ここでは便宜上タイムスタンプを直接使用しています。
6. イメージ名の最適化の実装
この処理は実際には古い画像を削除し、新しい画像の名前を指定された形式に変更するものであり、関数を記述することで実現できます。
6.1 イメージの重複排除、削除、名前変更
- 指定されたパスの下にあるすべての画像ファイルを検索し、走査します。
- まず、ID で指定されたファイルが存在するかどうかを確認し、存在する場合は削除します。
- 新しく保存したファイル名 (timestamp.jpg) に従って、対応するファイルを見つけて、その名前を timestamp.id.jpg に変更します
。upload.js に次のコードを記述します。
const fs = require('fs')
// 对图片进行去重删除和重命名
const hanldeImgDelAndRename = (id, filename, dirPath) => {
// TODO 查找该路径下的所有图片文件
fs.readdir(dirPath, (err, files) => {
for (let i in files) {
// 当前图片的名称
const currentImgName = path.basename(files[i])
// 图片的名称数组:[时间戳, id, 后缀]
const imgNameArr = currentImgName.split('.')
// TODO 先查询该id命名的文件是否存在,有则删除
if (imgNameArr[1] === id) {
const currentImgPath = dirPath + '/' + currentImgName
fs.unlink(currentImgPath, (err) => {
})
}
// TODO 根据新存入的文件名(时间戳.jpg),找到对应文件,然后重命名为: 时间戳.id.jpg
if (currentImgName === filename) {
const old_path = dirPath + '/' + currentImgName
const new_path = dirPath + '/' + imgNameArr[0] + '.' + id + path.extname(files[i])
// 重命名该文件
fs.rename(old_path, new_path, (err) => {
})
}
}
})
}
関数実行プロセスの分析:
- この関数は主に fs 組み込みモジュールの readdir を呼び出して、指定されたパスでファイルをクエリし、走査します。
- ピクチャ名を配列に分割し、idと受信idを取り出して判定し、fsの組み込みモジュールのfs.unlink()メソッドを呼び出して条件を満たしていればファイルを削除する
- 新しく保存したファイル名 (timestamp.jpg) に従って、対応するファイルを見つけて、その名前を timestamp.id.jpg に変更します。次に、fs 組み込みモジュールの fs.rename() メソッドを呼び出して、ファイルの名前を変更します。
6.2 UploadAvatarインターフェースの変更
hanldeImgDelAndRename メソッドの重複排除、削除、名前変更が完了したら、upload.js の元のアップロード インターフェイス メソッド UploadAvatar でこのメソッドを呼び出し、次のコードに変更する必要があります。
const path = require('path')
// 封装处理路径函数
const handlePath = (dir) => {
return path.join(__dirname, './', dir)
}
// 上传接口的 请求参数req 响应参数res
function uploadAvatar(req, res) {
return new Promise((resolve, reject) => {
multerConfig.single('file')(req, res, function (err) {
if (err) {
reject(err)
} else {
// 对图片进行去重删除和重命名
hanldeImgDelAndRename(req.body.id, req.file.filename, handlePath('../../public'))
const img = req.file.filename.split('.')
resolve({
id: req.body.id,
// 重新返回符合规定的图片链接地址
img_url: BaseURL + imgPath + img[0] + '.' + req.body.id + '.' + img[1]
})
}
})
})
}
注: ファイルをアップロードするときは、req.body.id が受信 ID を取得できるように、id フィールドを指定する必要があります。
7. 最終テスト
7.1 最初のアップロード
画像が正常にアップロードされ、画像の名前が当社の規定に従って結合され、バックエンド サーバーもアップロードされた画像を正常に保存していることがわかります。
7.2 2 番目のアップロード
2 回目のアップロードでは、同じ ID を持つ古い画像が正常に削除され、画像名が変更されました。
7.3 3回目のアップロード
ここでは、次のように、さまざまな ID をアップロードして、さまざまなユーザーがテスト用にアバターをアップロードすることを示すこともできます。
異なる ID 間でアップロードされた写真は相互に干渉せず、ID が一致した場合にのみ置き換えられ、名前が変更されることがわかります。最後に、取得した画像リンク アドレスをコントローラ内のデータベースに保存するだけでよく、ユーザー ID に従って保存できます。
8. ajaxアップロード
<div class="ajax">
<p>ajax上传</p>
<form>
<input type="text" name="username" />
<input type="password" name="password" />
<input type="file" name="avatar" />
<button type="button">上传</button>
</form>
<img />
</div>
<script>
let btn = document.querySelector('.ajax [type=button]');
var username = document.querySelector('.ajax [name=username]');
var password = document.querySelector('.ajax [name=password]');
var avatar = document.querySelector('.ajax [name=avatar');
avatar.addEventListener('change', () => {
// 创建预览地址
let httpUrl = window.webkitURL.createObjectURL(new Blob(avatar.files));
document.querySelector('img').src = httpUrl;
});
btn.addEventListener('click', () => {
// 要处理成表单对象上传
const formsdata = new FormData();
formsdata.append('username', username.value);
formsdata.append('password', password.value);
// 追加name值,和文件对象
formsdata.append('avatar', avatar.files[0]);
axios
.post('/user/upload/avatar', formsdata, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then(res => {
document.querySelector('img').src = res.data.imgPath;
});
});
</script>