克隆Airbnb,上传房屋图片

100 阅读5分钟

这篇文章是一个新系列的一部分,我们用Next.js建立一个克隆的Airbnb。请看第一篇文章

在新的房屋表格中,以及在编辑现有房屋时,我们现在最怀念的一件事是能够上传图片。

目前,我们只能在某个地方上传图片,然后在表格中粘贴URL。这对我们的用户来说不是很实用。

让我们来添加这个功能。

在我们开始之前,我们必须添加一个npm包,叫做express-fileupload

npm install express-fileupload

我们把它作为一个中间件添加到Express中,在server.js

const fileupload = require('express-fileupload')
server.use(
  //...
  fileupload()
)

这是有必要的,因为否则服务器就不能解析文件上传。

接下来,在components/HouseForm.js ,改变我们使用的当前输入字段。

<p>
  <label>House picture URL</label>
  <input required onChange={event => setPicture(event.target.value)} type='text'
  placeholder='House picture URL' value={picture} />
</p>

改为这个文件上传和图像显示的组合。

<p>
  <label>House picture</label>
  <input type="file" id="fileUpload" />
  {picture ? <img src="{picture}" width="200" alt="House image" /> : ''}
</p>

如果你尝试重新加载页面,文件选取器应该会出现在那里让我们在底部已经有的表单的CSS样式中添加input[type=file]

<style jsx > {
  ` input[type='number'],
  input[type='file'],
  select,
  textarea {
    /*... */
  }
}

让我们也限制文件输入,只接受图片。

<input type="file" id="fileUpload" accept="image/*" />

见https://flaviocopes.com/how-to-accept-images-file-input/

现在我们必须处理这个输入字段的change 事件。

<input
  type='file'
  id='fileUpload'
  accept='image/*'
  onChange={async (event) => {
    const files = event.target.files
    const formData = new FormData()
    formData.append('image', files[0])

    const response = await axios.post('/api/host/image', formData)
    setPicture('http://localhost:3000' + response.data.path)
  }}
/>

当文件输入发生变化时,这个事件会被调用(一个图像被选中)。在这里,我们从event.target.files ,并将其POST到/host/image ,这是我们接下来要做的一个新的端点。

我们期待一个path 的属性回来,这将是我们图片的URL,我们使用setPicture 钩子的更新函数来分配它。

现在让我们在server.js 中制作POST/api/host/image 端点。

我们首先检查用户是否已经登录,并从请求中获得图片。

server.js

server.post('/api/host/image', (req, res) => {
  if (!req.session.passport) {
    res.writeHead(403, {
      'Content-Type': 'application/json',
    })
    res.end(
      JSON.stringify({
        status: 'error',
        message: 'Unauthorized',
      })
    )

    return
  }

  const image = req.files.image
})

接下来我们运行npm install randomstring ,并导入该模块。

const randomstring = require('randomstring')

我们需要它为我们的图片生成一个随机字符串,因为用户可能会提交相同名称的图片。我只是要在图片原名前加上一个随机字符串,但在现实世界中,你可能想完全随机化,而且在覆盖文件之前还要检查是否已经有了这个名字。

const fileName = randomstring.generate(7) + image.name.replace(/\s/g, '')
const path = __dirname + '/public/img/' + fileName

然后我们调用上传图片的mv 属性。那是由express-fileupload 模块提供给我们的。我们把它移到path ,然后我们把成功(或错误!)传回给客户。

image.mv(path, (error) => {
  if (error) {
    console.error(error)
    res.writeHead(500, {
      'Content-Type': 'application/json',
    })
    res.end(JSON.stringify({ status: 'error', message: error }))
    return
  }

  res.writeHead(200, {
    'Content-Type': 'application/json',
  })
  res.end(JSON.stringify({ status: 'success', path: '/img/' + fileName }))
})

这就是我们的/api/host/image 端点的完整代码。

server.post('/api/host/image', (req, res) => {
  if (!req.session.passport) {
    res.writeHead(403, {
      'Content-Type': 'application/json',
    })
    res.end(
      JSON.stringify({
        status: 'error',
        message: 'Unauthorized',
      })
    )

    return
  }

  const image = req.files.image
  const fileName = randomstring.generate(7) + image.name.replace(/\s/g, '')
  const path = __dirname + '/public/img/' + fileName

  image.mv(path, (error) => {
    if (error) {
      console.error(error)
      res.writeHead(500, {
        'Content-Type': 'application/json',
      })
      res.end(JSON.stringify({ status: 'error', message: error }))
      return
    }

    res.writeHead(200, {
      'Content-Type': 'application/json',
    })
    res.end(JSON.stringify({ status: 'success', path: '/img/' + fileName }))
  })
})

现在你应该能够成功地为房子提交一个新的图片,也可以更新现有的房子图片

太棒了,我们达到了项目的终点

在这样一个应用程序中,我们有很多东西需要建立。

我们也有很多东西我们仍然错过,只是为了给我们的用户提供一个基本的房屋预订市场。

在我的脑海中,我们目前错过了增加主人和客人之间的沟通,允许添加多张图片和管理画廊,管理超级主人,允许不同的日期有不同的价格,阻隔天数。

我们需要一年的时间来完成所有这些。

也许在未来的续集中!