mongoose 通过客户端上传并从API检索后,图像未显示

2w3rbyxf  于 2023-03-30  发布在  Go
关注(0)|答案(1)|浏览(138)

我目前正在构建一个React JS应用程序,该应用程序与我同时使用Node JS构建的API进行通信,并且我遇到了上传图像并正确显示的问题。我推测问题源于应用程序如何处理上传图像。
用户通过以下形式上传头像:

<form
                id="RegistrationForm"
                ref={FormRef}
                encType="multipart/form-data"
                className={`bg-[#f2e798] w-11/12 md:w-9/12 mx-auto lg:w-6/12 mt-[20px] py-10 rounded box_shadow`}
                onSubmit={(evt) => {
                    evt.preventDefault(); 
                    const ImageInputElem = ImageInputRef.current; 
                    UploadNewProfilePic(uploadURL, ImageInputElem, setPictureError)
                }}
            >
                <div className= "FormStyle w-11/12 mx-auto grid">
                    <label htmlFor="profile_pic">Profile picture</label>
                    <input
                        name="profile_pic"
                        id="profile_picInput"
                        ref={ImageInputRef}
                        type="file"
                        placeholder="Upload an image htmlFor your your profile picture here"
                        className="text-lg file:rounded-lg file:font-['DecoTech'] file:bg-[#99cbae] file:text-white cursor-pointer border-black border-[1px] rounded"
                        onChange={(evt) => { HandleFileChange(evt, setImage) }}
                    />
                    <FormButtons />
                </div>
                <div
                    id="pictureError"
                    className="ErrorDiv"
                    ref={ImageErrorRef}>
                    {pictureError != null && pictureError.length > 0 && RenderError(pictureError)}
                </div>
        </form>

下面的钩子用于在用户提交表单时将fetch请求发送到服务器:

const UploadNewProfilePic = async (apiURL, ImageInputElem, setPictureError) => {
        const formData = new FormData; 
        formData.append("profile_pic", ImageInputElem.files[0])
        try {
            await fetch(apiURL,
                {
                    method: "PUT",
                    body: formData,
                }
            )
                .then(async response => {
                    const result = await response.json();

                    if (response.ok) {
                        console.log("Uploaded image successfully: ", result.message)
                        GoHome();
                    }
                    else {
                        console.log("Upload failed: ", result.error)
                        RenderErrorArray(result.error, setPictureError);
                    }
                })
        } catch (e) {
            console.log("Error uploading file:", error);
            RenderErrorArray([{ para: "file upload error", msg: `Upload error: ${error}` }], setPictureError);
        }
    }

在服务器端,路由设置如下:

const express = require('express');
const router = express.Router(); 
const cors = require('cors'); 
const UserController = require('../controller/userController.js'); 
const path = require('path')
const multer = require('multer');

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, "public/uploads")
    },
    filename: function (req, file, cb) {
        const ext = path.extname(file.originalname);
        const filename = `${Date.now()}-${file.filename}${ext}`;
        cb(null, filename)
    },
});

const upload = multer({
    limits: { fileSize: 1024 * 1024 * 5 },
    storage: storage,
    fileFilter: function (req, file, cb) {
        if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) {
            return cb(new Error('Only image files are allowed!'));
        }
        cb(null, true);
    }
}); 


router.put('/users/:id/uploadnewpicture', cors(), upload.single("profile_pic"), UserController.UploadNewProfilePicture)

上传图片的存储类型为buffer类型:

const User = new Schema({
    //...
    profile_pic: { data: Buffer, contentType: String },
     //...

})

以下控制器函数用于处理PUT请求:

exports.UploadNewProfilePicture = (req, res, next) => {
    try {
        const { BufferImage } = dataHooks();          
        const BufferedImg = {
            data: fs.readFileSync(path.join(__dirname, '../public/uploads/', req.file.filename)),
            contentType: req.file.mimetype,
        }
        const updates = {
            _id: req.params.id,
            profile_pic: BufferedImg, 
        }
        const updateUser = new User(updates)
        User.findByIdAndUpdate(req.params.id, updateUser)
            .then(() => {
                return res.status(200).json({message: "Profile picture has been successfully updates."})
            })
            .catch(e => {
                return res.status(404).json({ error: [{ param: "server", msg: `Error in updating profile picture: ${e}.` }] })
            })
    } catch (e) {
        console.log("Error in uploading new profile picture: ", e)
        return res.status(500).json({ error: [{para: "server error", msg: `Upload error: ${e}`}]})
    } 
}

然后,回到客户端,当应用呈现呈现配置文件图像的页面时,它执行以下钩子从服务器检索图像:

const FetchProfilePic = async (apiURL, userID, dispatch) => {
        await fetch(apiURL,
            {
                method: "GET",
            }
        )
            .then(async response => {
                if (response.ok) {
                    await response.json()
                        .then(result => {
                            localStorage.setItem("ProfilePicture", JSON.stringify(result.profile_pic))
                            dispatch(result.profile_pic)
                        })
                }
                else {
                    const result = await response.json();
                    console.log("Error in fetching image: ", result.error)
                }
            })
    }

最后,它使用以下钩子来渲染配置文件图像:

const RenderProfilePic = props => {
    const { profile_pic } = props;
    const ImageStyle = `select-none w-[270px] h-[270px] object-cover rounded-full mx-auto`;
    const ImageWrapperStyle = `m-auto overflow-hidden relative z-[1]`; 
    console.log("profile_pic: ", typeof profile_pic.data)
    const binaryData = new Uint8Array(profile_pic.data);
    const base64Image = btoa(String.fromCharCode.apply(null, binaryData));

    //const base64Image = profile_pic.data.toString('base64');

    const dataURL = `data:${profile_pic.contentType};base64,${base64Image}`;
    try {
        return (
            <div
                id="ImageWrapper"
                className={ImageWrapperStyle}
            >
                <img src={dataURL} alt="Profile Picture" className={`${ImageStyle}`} />
            </div>
        )
    } catch (e) {
        console.log("error in rendering image: ", e)
    }
}

我注意到的是,每当客户端从服务器检索图像时,图像的类型都是作为对象而不是类型缓冲区。

console.log(typeof profile_pic) // returns object

如果在座的任何人能给予我一些关于如何解决这个问题的指导,我将不胜感激。
Github for client site
Github for server site

uqxowvwt

uqxowvwt1#

最后,我在别人的帮助下解决了这个问题。
问题是缓冲区的数据不能在Node外部解释,所以需要先转换为base64。
我实现了以下函数:

function toBase64(arr) {
        console.log("toBase64 arr: ", arr)
        return btoa(
            arr.reduce((data, byte) => data + String.fromCharCode(byte), '')
        );
    }

当应用程序从服务器接收到缓冲区数据并将其转换为base64时,它会触发。

相关问题