从REST API返回的图像始终显示为损坏 [英] Image returned from REST API always displays broken

查看:71
本文介绍了从REST API返回的图像始终显示为损坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用React构建用于艺术品投资组合应用程序的内容管理系统.客户端将发布到使用Mongoose插入到MongoDB中的API.然后,API向数据库查询新插入的图像,并将其返回给客户端.

I am building a content management system for an art portfolio app, with React. The client will POST to the API which uses Mongoose to insert into a MongoDB. The API then queries the DB for the newly inserted image, and returns it to the client.

这是我的使用Mongoose连接到MongoDB的代码:

Here's my code to connect to MongoDB using Mongoose:

mongoose.connect('mongodb://localhost/test').then(() => 
console.log('connected to db')).catch(err => console.log(err))

mongoose.Promise = global.Promise

const db = mongoose.connection

db.on('error', console.error.bind(console, 'MongoDB connection error:'))

const Schema = mongoose.Schema;

const ImgSchema = new Schema({
  img: { data: Buffer, contentType: String }
})

const Img = mongoose.model('Img', ImgSchema)

我正在使用multer和fs处理图像文件.我的POST端点看起来像这样:

I am using multer and fs to handle the image file. My POST endpoint looks like this:

router.post('/', upload.single('image'), (req, res) => {
  if (!req.file) {
    res.send('no file')
  } else {
    const imgItem = new Img()
    imgItem.img.data = fs.readFileSync(req.file.path)
    imgItem.contentType = 'image/png'
    imgItem
      .save()
      .then(data => 
        Img.findById(data, (err, findImg) => {
          console.log(findImg.img)
          fs.writeFileSync('api/uploads/image.png', findImg.img.data)
          res.sendFile(__dirname + '/uploads/image.png')
        }))
  } 
})

我可以在文件结构中看到writeFileSync正在将映像写入磁盘. res.sendFile抓取它并将其发送给客户端.

I can see in the file structure that writeFileSync is writing the image to the disk. res.sendFile grabs it and sends it down to the client.

客户端代码如下:

handleSubmit = e => {
    e.preventDefault()
    const img = new FormData()
    img.append('image', this.state.file, this.state.file.name)
    axios
      .post('http://localhost:8000/api/gallery', img, {
        onUploadProgress: progressEvent => {
          console.log(progressEvent.loaded / progressEvent.total)
        }
      })
      .then(res => {
        console.log('responsed')
        console.log(res)
        const returnedFile = new File([res.data], 'image.png', { type: 'image/png' })
        const reader = new FileReader()
        reader.onloadend = () => {
          this.setState({ returnedFile, returned: reader.result })
        }
        reader.readAsDataURL(returnedFile)
      })
      .catch(err => console.log(err))
  }

这确实将返回的文件和img数据url都置于了状态.但是,在我的应用程序中,图像始终显示为损坏.

This does successfully place both the returned file and the img data url on state. However, in my application, the image always displays broken.

以下是一些屏幕截图:

该如何解决?

推荐答案

避免发送回base64编码的图像(多个图像+大文件+大编码字符串=非常慢的性能).我强烈建议创建一个微服务,该微服务仅处理图像上载以及任何其他与图像相关的获取/发布/放置/删除请求.将其与主应用程序分开.

Avoid sending back base64 encoded images (multiple images + large files + large encoded strings = very slow performance). I'd highly recommend creating a microservice that only handles image uploads and any other image related get/post/put/delete requests. Separate it from your main application.

例如:

  • 我使用multer创建图像缓冲区
  • 然后使用Sharp或fs保存图像(取决于文件类型)
  • 然后我将文件路径发送到控制器以保存到数据库中
  • 然后,前端在尝试访问时发出GET请求:http://localhost:4000/uploads/timestamp-randomstring-originalname.fileext
  • I use multer to create an image buffer
  • Then use sharp or fs to save the image (depending upon file type)
  • Then I send the filepath to my controller to be saved to my DB
  • Then, the front-end does a GET request when it tries to access: http://localhost:4000/uploads/timestamp-randomstring-originalname.fileext

简单来说,我的微服务就像CDN一样,仅用于图像.

例如,用户使用某些FormData向http://localhost:4000/api/avatar/create发送发布请求:

For example, a user sends a post request to http://localhost:4000/api/avatar/create with some FormData:

它首先通过一些Express中间件:

It first passes through some Express middlewares:

libs/middlewares.js

...
app.use(cors({credentials: true, origin: "http://localhost:3000" })) // allows receiving of cookies from front-end

app.use(morgan(`tiny`)); // logging framework

app.use(multer({
        limits: {
            fileSize: 10240000,
            files: 1,
            fields: 1
        },
        fileFilter: (req, file, next) => {
            if (!/\.(jpe?g|png|gif|bmp)$/i.test(file.originalname)) {
                req.err = `That file extension is not accepted!`
                next(null, false)
            }
            next(null, true);
        }
    }).single(`file`))

app.use(bodyParser.json()); // parses header requests (req.body)

app.use(bodyParser.urlencoded({ limit: `10mb`, extended: true })); // allows objects and arrays to be URL-encoded

...etc     

然后点击avatars路线:

routes/avatars.js

app.post(`/api/avatar/create`, requireAuth, saveImage, create);

然后通过一些用户身份验证,然后通过我的saveImage中间件:

It then passes through some user authentication, then goes through my saveImage middleware:

services/saveImage.js

const createRandomString = require('../shared/helpers');
const fs = require("fs");
const sharp = require("sharp");
const randomString = createRandomString();

if (req.err || !req.file) {
  return res.status(500).json({ err: req.err || `Unable to locate the requested file to be saved` })
  next();
}

const filename = `${Date.now()}-${randomString}-${req.file.originalname}`;
const filepath = `uploads/${filename}`;

const setFilePath = () => { req.file.path = filepath; return next();}

(/\.(gif|bmp)$/i.test(req.file.originalname))
    ? fs.writeFile(filepath, req.file.buffer, (err) => {
            if (err) { 
              return res.status(500).json({ err: `There was a problem saving the image.`}); 
              next();
            }

            setFilePath();
        })
    : sharp(req.file.buffer).resize(256, 256).max().withoutEnlargement().toFile(filepath).then(() => setFilePath())

如果文件被保存,它将发送req.file.path到我的create控制器.这将作为文件路径和图像路径保存到我的数据库中(保存avatarFilePath/uploads/imagefile.ext用于删除目的,保存avatarURL[http://localhost:4000]/uploads/imagefile.ext并将其用于前端GET请求) :

If the file is saved, it then sends a req.file.path to my create controller. This gets saved to my DB as a file path and as an image path (the avatarFilePath or /uploads/imagefile.ext is saved for removal purposes and the avatarURL or [http://localhost:4000]/uploads/imagefile.ext is saved and used for the front-end GET request):

controllers/avatars.js (我使用的是Postgres,但您可以替代Mongo)

controllers/avatars.js (I'm using Postgres, but you can substitute for Mongo)

create: async (req, res, done) => {
            try {
                const avatarurl = `${apiURL}/${req.file.path}`;

                await db.result("INSERT INTO avatars(userid, avatarURL, avatarFilePath) VALUES ($1, $2, $3)", [req.session.id, avatarurl, req.file.path]);

                res.status(201).json({ avatarurl });
            } catch (err) { return res.status(500).json({ err: err.toString() }); done(); 
        }

然后,当前端尝试通过<img src={avatarURL} alt="image" /><img src="[http://localhost:4000]/uploads/imagefile.ext" alt="image" />访问uploads文件夹时,它将由微服务提供服务:

Then when the front-end tries to access the uploads folder via <img src={avatarURL} alt="image" /> or <img src="[http://localhost:4000]/uploads/imagefile.ext" alt="image" />, it gets served up by the microservice:

库/server.js

const express = require("express");
const path = app.get("path");
const PORT = 4000;

//============================================================//
// EXPRESS SERVE AVATAR IMAGES
//============================================================//
app.use(`/uploads`, express.static(`uploads`));

//============================================================//
/* CREATE EXPRESS SERVER */
//============================================================//
app.listen(PORT);

记录请求时的外观:

19:17:54 INSERT INTO avatars(userid, avatarURL, avatarFilePath) VALUES ('08861626-b6d0-11e8-9047-672b670fe126', 'http://localhost:4000/uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png', 'uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png')

POST /api/avatar/create 201 109 - 61.614 ms

GET /uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png 200 3027 - 3.877 ms

用户在成功执行GET请求后看到的内容:

What the user sees upon successful GET request:

这篇关于从REST API返回的图像始终显示为损坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆