在集成环信IM功能时,会涉及到需要备份消息到本地服务器这样的需求,此文章讲述了如何下载历史消息文件并逐条解析
1.环信提供了下载历史消息的接口,此接口按照1小时为单位,也就是每次下载历史消息记录,将会直接下载一个小时内的所有消息并存储为单个文件
2.直接下载下来的文件为压缩成gz格式的压缩文件
此文章展示了2种语言处理方式:JAVA,Python
一.java版本
1.需要集成1项:
fastjson
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.53</version>
</dependency>
2.代码如下
package org.jj.util;
import com.alibaba.fastjson.JSONObject;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class MessageLogHelper {
/**
* restapi : ❌需要根据实际appkey确定是哪一个地址
* appkey : ❌请填入你的appkey
* app_token : ❌请填入你的APP TOKEN
*/
static String restapi = "a1.easemob.com";
static String appkey = "easemob-demo#support";
static String orgName = appkey.split("#")[0];
static String appName = appkey.split("#")[1];
static String baseurl = "https://" + restapi + "/" + orgName + "/" + appName;
static String app_token = "❎这里需要填入你的APP TOKEN";
public static String fetchMessageLogFileURL(String time) {
String urlString = baseurl + "/chatmessages/" + time;
try {
URL url = new URL(urlString); // 替换为你想访问的URL
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "bearer " + app_token);
// 检查响应码,例如 200 表示成功
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 打印结果
System.out.println(response);
JSONObject jo = JSONObject.parseObject(response.toString());
return jo.getJSONArray("data").getJSONObject(0).getString("url");
} else {
System.out.println("GET request not worked");
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
*
* @Title: getInputStream
* @Description: TODO 获取网络连接的InputStream
* @return
* @throws IOException
* @CreateDate:2021 Nov 2 20:56:56
*/
private static InputStream getInputStream(String urlString) throws IOException {
InputStream inputStream=null;
HttpURLConnection httpurlconn=null;
try {
URL url=new URL(urlString);
if(url!=null) {
httpurlconn=(HttpURLConnection) url.openConnection();
//设置连接超时时间
httpurlconn.setConnectTimeout(3000);
//表示使用GET方式请求
httpurlconn.setRequestMethod("GET");
httpurlconn.setRequestProperty("Authorization", "authorization");
int responsecode=httpurlconn.getResponseCode();
if(responsecode==200) {
//从服务返回一个输入流
System.out.println("成功");
inputStream=httpurlconn.getInputStream();
}else {
System.out.println("失败" + responsecode);
}
}
}catch (MalformedURLException e) {
e.printStackTrace();
}
return inputStream;
}
/**
* 将InputStream写入本地文件
* @param destination 写入本地目录
* @param input 输入流
* @throws IOException
*/
private static void writeToLocal(String destination, InputStream input)
throws IOException {
byte[] bytes = new byte[1024];
FileOutputStream fileOut = new FileOutputStream(destination);
// int index = 0;
int length = 0;
while ((length = input.read(bytes)) != -1) {
fileOut.write(bytes, 0, length);
fileOut.flush();
// index ++;
}
fileOut.close();
input.close();
}
//下载文件到本地
public static void downloadFile(String urlString,String filePath) {
InputStream inputStream = null;
try {
inputStream = getInputStream(urlString);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
writeToLocal(filePath,inputStream);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println();
}
//解压gzip文件
public static void unGzipFile(String gzFilePath,String directoryPath) {
String ouputfile = "";
try {
//建立gzip压缩文件输入流
FileInputStream fin = new FileInputStream(gzFilePath);
//建立gzip解压工作流
GZIPInputStream gzin = new GZIPInputStream(fin);
//建立解压文件输出流
// ouputfile = sourcedir.substring(0,sourcedir.lastIndexOf('.'));
// ouputfile = ouputfile.substring(0,ouputfile.lastIndexOf('.'));
FileOutputStream fout = new FileOutputStream(directoryPath);
int num;
byte[] buf=new byte[1024];
while ((num = gzin.read(buf,0,buf.length)) != -1) {
fout.write(buf,0,num);
}
gzin.close();
fout.close();
fin.close();
} catch (Exception ex){
System.err.println(ex.toString());
}
return;
}
public static void test_parseMessages(String filePath) throws IOException {
FileInputStream inputStream = new FileInputStream(filePath);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String str = null;
long i = 0;
while((str = bufferedReader.readLine()) != null){
JSONObject jo = JSONObject.parseObject(str);
System.out.println("==========================================" + i);
System.out.println("消息id:" + jo.get("msg_id"));
System.out.println("发送id:" + jo.get("from"));
System.out.println("接收id:" + jo.get("to"));
System.out.println("服务器时间戳:" + jo.get("timestamp"));
System.out.println("会话类型:" + jo.get("chat_type"));
System.out.println("消息扩展:" + jo.getJSONObject("payload").get("ext"));
System.out.println("消息体:" + jo.getJSONObject("payload").getJSONArray("bodies").get(0));
i ++;
if (i > 100) break;
}
//close
inputStream.close();
bufferedReader.close();
}
//运行测试
public static void main(String[] args) {
String time = "2024123011";
String url = fetchMessageLogFileURL("2024123011");
System.out.println(url);
String g_path = "/Users/yj/Documents/讲解/消息记录导出并解析/java/" + time + ".gz";
String f_path = "/Users/yj/Documents/讲解/消息记录导出并解析/java/" + time + ".txt";
downloadFile(url, g_path);
unGzipFile(g_path, f_path);
try {
test_parseMessages(f_path);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
二.python版本
1.需要集成1项
pip install requests
import json
import requests
import gzip
# ❌需要根据实际appkey确定是哪一个地址
restapi = 'a1.easemob.com'
# ❌请填入你的appkey
appkey = 'easemob-demo#support'
org = appkey.split('#')[0]
app = appkey.split('#')[1]
baseurl = f'https://{restapi}/{org}/{app}'
# ❌请填入你的APP TOKEN
app_token = '<⚠️APP TOKEN>'
# 示例 如果需要获取2024年12月30日10时到11时的消息记录,则填入: '2024123010'
def getMessageLogFileURL(time):
url = f'{baseurl}/chatmessages/{time}'
headers = {
'Authorization': f'Bearer {app_token}',
'Content-Type': 'application/json',
}
response = requests.get(url, headers=headers)
json = response.json()
print("response = " , json)
url = json['data'][0]['url']
print("url = " , url)
return url
def downloadFile(url, filePath):
response = requests.get(url, stream=True) # 发送GET请求,stream参数指定以流的方式下载文件
if response.status_code == 200: # 检查响应状态码
with open(filePath, 'wb') as f: # 打开本地文件进行写入操作
for chunk in response.iter_content(chunk_size=8192): # 分块读取文件内容,每次读取1KB
if chunk: # 检查是否有数据块可读
f.write(chunk) # 将数据块写入本地文件
f.flush() # 刷新缓冲区,确保数据写入磁盘
print('文件下载完成!')
else:
print('下载失败,状态码:', response.status_code)
def unzipFile(zipPath,unzipPath):
g_file = gzip.GzipFile(zipPath)
# 创建gzip对象
with open(unzipPath, "wb+") as content:
content.write(g_file.read())
g_file.close()
def parseMessages(filePath):
with open(filePath, 'r') as file:
for line in file:
# 处理每一行,例如打印
# print(line.strip()) # strip()去除行尾换行符
jsonObj = json.loads(line)
print('================================')
print(f"消息id [{jsonObj['msg_id']}]")
print(f"消息from [{jsonObj['from']}]")
print(f"消息to [{jsonObj['to']}]")
print(f"消息timestamp [{jsonObj['timestamp']}]")
print(f"消息chat_type [{jsonObj['chat_type']}]")
print(f"消息content_type [{jsonObj['content_type']}]")
print(f"消息内容payload [{jsonObj['payload']}]")
# ❌这里获取的是2024年12月30日11点的 消息记录文件
time = '2024123011'
fileURL = getMessageLogFileURL(time)
directory = '/Users/yj/Documents/讲解/消息记录导出并解析/python'
gzipPath = f'{directory}/{time}.gz'
filePath = f'{directory}/{time}.txt'
# 下载
downloadFile(fileURL, gzipPath)
# 解压
unzipFile(gzipPath,filePath)
# 解析
parseMessages(filePath)
三、关于 restapi
appkey
app_token
获取方式:
appkey
格式说明:参考:https://doc.easemob.com/document/server-side/enable_and_configure_IM.html#获取环信即时通讯-im-的信息
一 打开页面
https://console.easemob.com/app/applicationOverview/detail
根据此页面,我们可获取到三个字段
appkey、Client ID、ClientSecret
appkey是由 orgname和appname构成,用符号#衔接
“Client ID” 和 “ClientSecret” 仅在获取apptoken时会用到
二 打开页面
https://console.easemob.com/app/im-service/detail
可获得字段 Rest Api
a1.easemob.com
最终字段收集完成
获取apptoken
文档地址:
https://doc.easemob.com/document/server-side/easemob_app_token.html
# 数据如下
# 1. appkey
# 1168171101115760#dabaojian
# 2. Client ID
# YXA6uUquUL6lEeeIi-vMCP7JJg
# 3. ClientSecret
# YXA6qb6yfKTMOhCdZibj9Q5Z9YNfzoA
# 4. restApi
# a1.easemob.com
# 下面这段代码为shell脚本代码,可直接复制并放在命令行(终端)中执行,可得到apptoken
curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{
"grant_type": "client_credentials",
"client_id": "YXA6uUquUL6lEeeIi-vMCP7JJg",
"client_secret": "YXA6qb6yfKTMOhCdZibj9Q5Z9YNfzoA",
"ttl": 1024000
}' 'https://a1.easemob.com/1168171101115760/dabaojian/token'
# ❌❌❌ 注意,以上所使用数据是一个测试appkey的数据,请使用自己的appkey和相关数据
结果如下
postman测试
apptoken使用示例如下:
示例所用接口:
获取单个用户详情,文档链接: https://doc.easemob.com/document/server-side/account_system.html#获取单个用户的详情
细节描述:
在header中加入:
"Authorization" : "Bearer YWMtNYtW0saVEe-nbnHeHt5XS1OaBn8hITJOq1PUZikodyC5Sq5QvqUR54iL68wI_skmAgMAAAGUFwSOzAAPoADMuiOI4HZ6xjlpgz7DOhyf9dRtSB8bqxy9_ZXbc4xb_Q"
这里需要格外注意的点:
1 key为 Authorization
2 value 有个前缀 Bearer,并且和 <access_token> 之间用一个空格衔接