这篇文章是一个新系列的一部分,我们用Next.js建立一个克隆的Airbnb。请看第一篇文章。
- 第一部分。让我们从安装Next.js开始
- 第2部分:建立房屋列表
- 第3部分:建立房屋详细视图
- 第4部分:CSS和导航栏
- 第5部分:从日期选择器开始
- 第6部分:添加侧边栏
- 第7部分:添加 react-day-picker
- 第8部分。在页面中添加日历
- 第9部分:配置DayPickerInput组件
- 第10部分:同步开始和结束日期
- 第11部分:显示所选日期的价格
- 第12部分:登录和注册表格
- 第13部分:激活模态
- 第14部分:发送注册数据到服务器
- 第15部分:添加postgres
- 第16部分:实现模型和DB连接
- 第17部分:创建一个会话令牌
- 第18部分:实现登录
- 第19部分:确定我们是否已经登录了
- 第20部分:在我们登录后改变状态
- 第21部分:注册后登录
- 第22部分:创建模型并将数据移到数据库中
- 第23部分:使用数据库而不是文件
- 第24部分:处理预订
- 第25部分:处理预订的日期
- 第26部分:如果已经预订了,则防止预订
- 第27部分:添加Stripe进行支付
- 第28部分:克隆Airbnb,处理Stripe webhooks
- 第29部分:克隆的Airbnb,查看预订情况
- 第30部分:克隆的Airbnb,清理预订
- 第31部分:克隆的Airbnb,管理房屋
- 第32部分:复制的Airbnb,添加新房子
- 第33部分:复制的Airbnb,编辑房屋
- 第34部分:克隆的Airbnb,房屋描述的安全HTML
在新的房屋表格中,以及在编辑现有房屋时,我们现在最怀念的一件事是能够上传图片。
目前,我们只能在某个地方上传图片,然后在表格中粘贴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/*" />
现在我们必须处理这个输入字段的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 }))
})
})
现在你应该能够成功地为房子提交一个新的图片,也可以更新现有的房子图片

太棒了,我们达到了项目的终点
在这样一个应用程序中,我们有很多东西需要建立。
我们也有很多东西我们仍然错过,只是为了给我们的用户提供一个基本的房屋预订市场。
在我的脑海中,我们目前错过了增加主人和客人之间的沟通,允许添加多张图片和管理画廊,管理超级主人,允许不同的日期有不同的价格,阻隔天数。
我们需要一年的时间来完成所有这些。
也许在未来的续集中!