NEXTJSを使用してTwitterをクローンする(multerを使用して画像をアップロードする)


バックエンド


multer


Multierはファイルをアップロードするための  multipart/form-data  処理に使用するノード.jsのミドルウェアです.効率を最大限に高める.  busboy  に基づいて

設定

npm i multer

使用方法

storageディスクストレージエンジンは、ファイルをディスクに保存するすべての制御機能を提供します.limitsファイルサイズの制限に使用します.
const multer = require("multer");
const path = require("path");

const upload = multer({
  storage: multer.diskStorage({
    destination(req, file, done) {
      done(null, "uploads");
    },
    filename(req, file, done) {
      // file.originalname 파일 이름 (이재훈.png)
      const ext = path.extname(file.originalname); //확장자 추출(.png)
      const basename = path.basename(file.originalname); // 이름 추출 (이재훈)
      done(null, basename + new Date().getTime() + ext); // (이재훈213123123.png)
    },
  }),
  limits: { fieldSize: 20 * 1024 * 1024 }, // 20MB
});
destination  第二課  filename  の各見出しページがあります.どちらのオプションも、ファイルがどこに保存されるかを指定する関数です.destination  オプション:アップロードしたファイルを保存するフォルダを指定します.これは.  string  形状で与えることができます(はい.  '/tmp/uploads' ). もしも  destination  オプションが指定されていない場合、オペレーティングシステムは一時ファイルを格納するデフォルトディレクトリを使用します.filename  フォルダに格納されているファイル名を決定します.もしも  filename  ない場合は、各ファイルにランダムな名前が付けられますが、ファイル拡張子は除外されます.
router.post("/images", isLoggedIn, upload.array("image"), postImages);
req.filesにはimageというファイル情報が含まれています.
exports.postImages = (req, res, next) => {
  return res.json(req.files.map(v => v.filename));
};
ファイル名をフロントに送信します.
画像をUploadsフォルダに入れたので、フロントからファイルにアクセスできるようにする必要があります.
app.use("/", express.static(path.join(__dirname, "uploads")));

Upload Post


.none()


テキストフィールドのみを許可します.ファイルがアップロードされている場合、「LIMIT UNEXPECTED FILE」などのエラーコードが表示されます.これは.  upload.fields([])  同じ操作を行います.
router.post("/", isLoggedIn, upload.none(), createPost);
exports.createPost = async (req, res, next) => {
  try {
    const { content, image } = req.body;
    const { id: UserId } = req.user;
    const post = await Post.create({
      content,
      UserId,
    });
    if (image) {
      if (Array.isArray(image)) {
        // 이미지를 여러개 올리면 image: ["이.png", "재.png"]
        const dbImages = await Promise.all(
          image.map(imagePath => Image.create({ src: imagePath }))
        );
        await post.addImages(dbImages);
      } else {
        // 이미지를 하나만 올리면 image: "이.png"
        const dbImage = await Image.create({ src: image });
        await post.addImages(dbImage);
      }
    }
		...
フロントからPostのコンテンツと画像パスを受信し、Postを作成します.
画像があれば、画像が並べられている場合はPromise.allは画像を作成し、addImagesはpostと画像を接続します.

HashTag


さらに、ユーザがコンテンツに#などのハッシュタグを追加する場合、ハッシュタグも作成する必要がある.
exports.createPost = async (req, res, next) => {
  try {
    const { content, image } = req.body;
    const { id: UserId } = req.user;
    const hashtags = content.match(/#[^\s#]+/g);
    const post = await Post.create({
      content,
      UserId,
    });
    if (hashtags) {
      const result = await Promise.all(
        hashtags.map(hashtag =>
          Hashtag.findOrCreate({
            where: { name: hashtag.slice(1).toLowerCase() },
          })
        )
      ); // [[노드,true],[리액트,true]]
      await post.addHashtags(result.map(v => v[0]));
    }
	...
まずhashtagsが存在するかどうかを検索します.matchメソッドのパラメータを使用して正規表現を記述する場合は、対応する正規表現としてのみ列挙されます.
findOrCreateメソッドを使用して、配列に作成されたhashtagsをdbに保存します.findOrCreateクエリー・オプションを満たすアイテムが見つからない場合、findOrCreateメソッドはテーブルにアイテムを作成します.
戻り値は配列첫번째 인덱스であり、인스턴스를はインスタンスが存在するかどうかを示す.
最後に、addHashtagメソッドを使用してpostとhashtagを接続します.

フロントエンド


FormData

두번째 인덱스はFormDataオブジェクトで、Ajaxを使用してフォームを転送できます.
ページ切り替えなしにフォームデータを送信する場合は、すぐにFormDataオブジェクトを使用します.
append()メソッドを使用してキー値を追加します.
作成されたBooleanFromData.
const PostForm = () => {
  //...
  const onChangeImages = useCallback(e => {
    console.log("images", e.target.files);
    const imageFormData = new FormData();
    [].forEach.call(e.target.files, f => {
      imageFormData.append("image", f);
    });
    dispatch({
      type: UPLOAD_IMAGES_REQUEST,
      data: imageFormData,
    });
  });
  return (
    <Form
      style={{ margin: "10px 0 20px" }}
      encType="multipart/form-data"
      onFinish={onSubmitBtn}
    >
     {/* ... */
      <div>
        <input
          type="file"
          name="image"
          multiple
          hidden
          ref={imageInput}
          onChange={onChangeImages}
        />
       {/* ... */
    </Form>
  );
};

export default PostForm;
FromDataを介してdispatchのバックエンドに送信され、バックエンドで受信したファイル名がsagaに再送信される
function uploadImagesAPI(data) {
  return axios.post(`/post/images`, data);
}

function* uploadImages(action) {
  try {
    const result = yield call(uploadImagesAPI, action.data);
    yield put({
      type: UPLOAD_IMAGES_SUCCESS,
      data: result.data,
    });
  } catch (err) {
    console.error(err);
    yield put({
      type: UPLOAD_IMAGES_FAILURE,
      error: err.response.data,
    });
  }
}
受信したファイル名をformDataに入れます.
case UPLOAD_IMAGES_SUCCESS: {
	draft.imagePaths = action.data;
  draft.uploadImagesLoading = false;
  draft.uploadImagesDone = true;
  break;
}

Upload Post


現在のtextが空の場合、警告ウィンドウが表示されます.dispatchは、現在の国のstateFormDataであり、imagePathsでもある.
このようにして完成したappendをaction dataに入れ、contentを生成する.
const onSubmitBtn = useCallback(() => {
    if (!text || !text.trim()) {
      return alert("게시글을 작성하세요.");
    }
    const formData = new FormData();
    imagePaths.forEach(image => {
      formData.append("image", image);
    });
    formData.append("content", text);
    dispatch({
      type: ADD_POST_REQUEST,
      data: formData,
    });
  }, [text, imagePaths]);