基于 Swoole 开发实时在线聊天室(十四):发送图片消息
上篇教程我们演示了聊天室中文本/表情消息发布,今天我们来看看图片消息如何发布。
前端交互代码
我们还是从前端组件开始,在聊天室组件 Chat.Vue
中,客户端图片发送核心逻辑位于 fileup
方法中,我们需要对原来的代码进行调整以适配基于 Laravel + Swoole 的后端接口:
fileup() {
const that = this;
const file1 = document.getElementById('inputFile').files[0];
if (file1) {
const formdata = new window.FormData();
formdata.append('file', file1);
formdata.append('api_token', this.auth_token);
formdata.append('roomid', that.roomid);
this.$store.dispatch('uploadImg', formdata);
const fr = new window.FileReader();
fr.onload = function () {
const obj = {
username: that.userid,
src: that.src,
img: fr.result,
msg: '',
roomid: that.roomid,
time: new Date(),
api_token: that.auth_token
};
socket.emit('message', obj);
};
fr.readAsDataURL(file1);
this.$nextTick(() => {
this.container.scrollTop = 10000;
});
} else {
console.log('必须有文件');
}
},
当我们点击聊天室房间里的相机图标,即可弹开图片上传窗口:
选中图片后,就会调用上述 fileup
方法上传图片。
这里面涉及两块逻辑:首先会调用后端基于 HTTP 协议的接口上传图片并保存消息到 messages
表,上传成功后才会发送消息到 Websocket 服务器,再由 Websocket 服务器将消息广播给所有在线用户。
上传图片对应的是这行代码:
this.$store.dispatch('uploadImg', formdata);
最终调用后端接口的代码位于 resources/js/api/server.js
中:
// 上传图片
postUploadFile: data => Axios.post('/file/uploadimg', data, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}),
我们马上会在后端编写这个接口。
基于 Websocket 发送图片消息对应的是这行代码:
socket.emit('message', obj);
这与之前发送文本消息的代码并无区别,无非是 obj
里面新增了 img
字段而已。
图片上传接口
接下来,我们在 Laravel 后端编写图片上传接口。
在 routes/api.php
中新增路由 file/uploadimg
:
Route::middleware('auth:api')->group(function () {
...
Route::post('/file/uploadimg', 'FileController@uploadImage');
}
然后通过 Artisan 命令创建控制器 FileController
:
php artisan make:controller FileController
在新生成的控制器文件 app/Http/Controllers/FileController.php
中编写 uploadImage
实现代码如下:
<?php
namespace App\Http\Controllers;
use App\Message;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class FileController extends Controller
{
public function uploadImage(Request $request)
{
if (!$request->hasFile('file') || !$request->file('file')->isValid() || !$request->has('roomid')) {
return response()->json([
'data' => [
'errno' => 500,
'msg' => '无效的参数(房间号/图片文件为空或者无效)'
]
]);
}
$image = $request->file('file');
$time = time();
$filename = md5($time . mt_rand(0, 10000)) . '.' . $image->extension();
$path = $image->storeAs('images/' . date('Y/m/d', $time), $filename, ['disk' => 'public']);
if ($path) {
// 图片上传成功则将对应图片消息保存到 messages 表
$message = new Message();
$message->user_id = auth('api')->id();
$message->room_id = $request->post('roomid');
$message->msg = ''; // 文本消息留空
$message->img = Storage::disk('public')->url($path);
$message->created_at = Carbon::now();
$message->save();
return response()->json([
'data' => [
'errno' => 200,
'msg' => '保存成功'
]
]);
} else {
return response()->json([
'data' => [
'errno' => 500,
'msg' => '文件上传失败,请重试'
]
]);
}
}
}
这里主要涉及到图片上传和消息保存逻辑。由于我们将图片保存到了 storage/public
目录下,为了让图片可以通过 Web URL 请求到,需要为 storage
目录创建软链:
php artisan storage:link
Websocket 服务端广播
最后我们在 routes/websocket.php
的 message
频道中补充图片消息处理逻辑:
WebsocketProxy::on('message', function (WebSocket $websocket, $data) {
...
// 获取消息内容
$msg = $data['msg'];
$img = $data['img'];
$roomId = intval($data['roomid']);
$time = $data['time'];
// 消息内容(含图片)或房间号不能为空
if((empty($msg) && empty($img))|| empty($roomId)) {
return;
}
// 记录日志
Log::info($user->name . '在房间' . $roomId . '中发布消息: ' . $msg);
// 将消息保存到数据库(图片消息除外,因为在上传过程中已保存)
if (empty($img)) {
$message = new Message();
$message->user_id = $user->id;
$message->room_id = $roomId;
$message->msg = $msg; // 文本消息
$message->img = ''; // 图片消息留空
$message->created_at = Carbon::now();
$message->save();
}
// 将消息广播给房间内所有用户
$room = Count::$ROOMLIST[$roomId];
$messageData = [
'userid' => $user->email,
'username' => $user->name,
'src' => $user->avatar,
'msg' => $msg,
'img' => $img,
'roomid' => $roomId,
'time' => $time
];
$websocket->to($room)->emit('message', $messageData);
...
非常简单,只是在之前广播消息的字段中添加了客户端上传的图片消息字段,没有任何其它逻辑。
至此,我们就可以完成了图片消息发送相关的前后端代码,接下来,我们在聊天室界面测试下图片消息的发送。
测试图片消息发布
开始之前,重新编译前端资源:
npm run dev
使前端代码修改生效。以及重启 Swoole HTTP 及 WebSocket 服务器:
bin/laravels restart
让后端代码修改生效。
然后,分别在 Chrome 和 Firefox 浏览器打开聊天室,登录并进入同一个房间,即可相互实时发送图片消息:
到这里,我们已经完成了聊天室的主体功能,接下来,我们将优化项目代码,尤其是后端 WebSocket 通信的性能和优雅性。
4 Comments
学院君幸苦了
这么个小项目竟然断断续续花了四个月。。。接下来要引以为戒,提高输出教程的可持续性 ?
请教一个问题,在swoole中laravel的数据库链接,超过一段时间会断开嘛? 如果断开怎么处理?谢谢。
支持长连接的