diff --git a/src/core/apis/packet.ts b/src/core/apis/packet.ts
index 7e65f930..d381c225 100644
--- a/src/core/apis/packet.ts
+++ b/src/core/apis/packet.ts
@@ -12,7 +12,12 @@ import { LogWrapper } from "@/common/log";
 import { SendLongMsgResp } from "@/core/packet/proto/message/action";
 import { PacketMsg } from "@/core/packet/msg/message";
 import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
-import { PacketMsgPicElement, PacketMsgPttElement, PacketMsgVideoElement } from "@/core/packet/msg/element";
+import {
+    PacketMsgFileElement,
+    PacketMsgPicElement,
+    PacketMsgPttElement,
+    PacketMsgVideoElement
+} from "@/core/packet/msg/element";
 
 
 interface OffsetType {
@@ -134,6 +139,12 @@ export class NTQQPacketApi {
                         peerUid: groupUin ? String(groupUin) : this.core.selfInfo.uid
                     }, e));
                 }
+                if (e instanceof PacketMsgFileElement){
+                    reqList.push(this.packetSession?.highwaySession.uploadFile({
+                        chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C,
+                        peerUid: groupUin ? String(groupUin) : this.core.selfInfo.uid
+                    }, e));
+                }
             }
         }
         return Promise.all(reqList);  // TODO: use promise.allSettled
diff --git a/src/core/packet/highway/session.ts b/src/core/packet/highway/session.ts
index cb4d5bd4..b5b2b178 100644
--- a/src/core/packet/highway/session.ts
+++ b/src/core/packet/highway/session.ts
@@ -8,10 +8,17 @@ import { HttpConn0x6ff_501Response } from "@/core/packet/proto/action/action";
 import { PacketHighwayClient } from "@/core/packet/highway/client";
 import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
 import { OidbSvcTrpcTcpBaseRsp } from "@/core/packet/proto/oidb/OidbBase";
-import { PacketMsgPicElement, PacketMsgPttElement, PacketMsgVideoElement } from "@/core/packet/msg/element";
-import { NTV2RichMediaHighwayExt } from "@/core/packet/proto/highway/highway";
+import {
+    PacketMsgFileElement,
+    PacketMsgPicElement,
+    PacketMsgPttElement,
+    PacketMsgVideoElement
+} from "@/core/packet/msg/element";
+import { FileUploadExt, NTV2RichMediaHighwayExt } from "@/core/packet/proto/highway/highway";
 import { int32ip2str, oidbIpv4s2HighwayIpv4s } from "@/core/packet/highway/utils";
-import { calculateSha1StreamBytes } from "@/core/packet/utils/crypto/hash";
+import { calculateSha1, calculateSha1StreamBytes, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
+import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
+import { OidbSvcTrpcTcp0XE37_800Response, OidbSvcTrpcTcp0XE37Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
 
 export const BlockSize = 1024 * 1024;
 
@@ -22,6 +29,7 @@ interface HighwayServerAddr {
 
 export interface PacketHighwaySig {
     uin: string;
+    uid: string;
     sigSession: Uint8Array | null
     sessionKey: Uint8Array | null
     serverAddr: HighwayServerAddr[]
@@ -40,6 +48,7 @@ export class PacketHighwaySession {
         this.logger = logger;
         this.sig = {
             uin: this.packetClient.napCatCore.selfInfo.uin,
+            uid: this.packetClient.napCatCore.selfInfo.uid,
             sigSession: null,
             sessionKey: null,
             serverAddr: [],
@@ -117,6 +126,17 @@ export class PacketHighwaySession {
         }
     }
 
+    async uploadFile(peer: Peer, file: PacketMsgFileElement): Promise<void> {
+        await this.checkAvailable();
+        if (peer.chatType === ChatType.KCHATTYPEGROUP) {
+            await this.uploadGroupFileReq(Number(peer.peerUid), file);
+        } else if (peer.chatType === ChatType.KCHATTYPEC2C) {
+            await this.uploadC2CFileReq(peer.peerUid, file);
+        } else {
+            throw new Error(`[Highway] unsupported chatType: ${peer.chatType}`);
+        }
+    }
+
     private async uploadGroupImageReq(groupUin: number, img: PacketMsgPicElement): Promise<void> {
         const preReq = await this.packer.packUploadGroupImgReq(groupUin, img);
         const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0x11c4_100', preReq, true);
@@ -400,4 +420,142 @@ export class PacketHighwaySession {
         }
         ptt.msgInfo = preRespData.upload.msgInfo;
     }
+
+    private async uploadGroupFileReq(groupUin: number, file: PacketMsgFileElement): Promise<void> {
+        file.isGroupFile = true;
+        file.fileMd5 = await computeMd5AndLengthWithLimit(file.filePath);
+        file.fileSha1 = await calculateSha1(file.filePath);
+        const preReq = await this.packer.packUploadGroupFileReq(groupUin, file);
+        const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0x6d6_0', preReq, true);
+        const preResp = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(
+            Buffer.from(preRespRaw.hex_data, 'hex')
+        );
+        const preRespData = new NapProtoMsg(OidbSvcTrpcTcp0x6D6Response).decode(preResp.body);
+        if (!preRespData?.upload?.boolFileExist) {
+            this.logger.logDebug(`[Highway] uploadGroupFileReq file not exist, need upload!`);
+            const ext = new NapProtoMsg(FileUploadExt).encode({
+                unknown1: 100,
+                unknown2: 1,
+                entry: {
+                    busiBuff: {
+                        senderUin: BigInt(this.sig.uin),
+                        receiverUin: BigInt(groupUin),
+                        groupCode: BigInt(groupUin),
+                    },
+                    fileEntry: {
+                        fileSize: BigInt(file.fileSize),
+                        md5: file.fileMd5,
+                        md5S2: file.fileMd5,
+                        checkKey: preRespData.upload.checkKey,
+                        fileId: preRespData.upload.fileId,
+                        uploadKey: preRespData.upload.fileKey,
+                    },
+                    clientInfo: {
+                        clientType: 3,
+                        appId: "100",
+                        terminalType: 3,
+                        clientVer: "1.1.1",
+                        unknown: 4
+                    },
+                    fileNameInfo: {
+                        fileName: file.fileName
+                    },
+                    host: {
+                        hosts: [
+                            {
+                                url: {
+                                    host: preRespData.upload.uploadIp,
+                                    unknown: 1,
+                                },
+                                port: preRespData.upload.uploadPort,
+                            }
+                        ]
+                    }
+                },
+                unknown200: 0,
+            })
+            await this.packetHighwayClient.upload(
+                71,
+                fs.createReadStream(file.filePath, {highWaterMark: BlockSize}),
+                file.fileSize,
+                file.fileMd5,
+                ext
+            );
+        } else {
+            this.logger.logDebug(`[Highway] uploadGroupFileReq file exist, don't need upload!`);
+        }
+        file.fileUuid = preRespData.upload.fileId;
+    }
+
+    private async uploadC2CFileReq(peerUid: string, file: PacketMsgFileElement): Promise<void> {
+        file.isGroupFile = false;
+        file.fileMd5 = await computeMd5AndLengthWithLimit(file.filePath);
+        file.fileSha1 = await calculateSha1(file.filePath);
+        const preReq = await this.packer.packUploadC2CFileReq(this.sig.uid, peerUid, file);
+        const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0xe37_1700', preReq, true);
+        const preResp = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(
+            Buffer.from(preRespRaw.hex_data, 'hex')
+        );
+        console.log("OidbSvcTrpcTcp.0xe37_1700", preRespRaw);
+        const preRespData = new NapProtoMsg(OidbSvcTrpcTcp0XE37Response).decode(preResp.body);
+        if (!preRespData.upload?.boolFileExist) {
+            this.logger.logDebug(`[Highway] uploadC2CFileReq file not exist, need upload!`);
+            const ext = new NapProtoMsg(FileUploadExt).encode({
+                unknown1: 100,
+                unknown2: 1,
+                entry: {
+                    busiBuff: {
+                        senderUin: BigInt(this.sig.uin),
+                    },
+                    fileEntry: {
+                        fileSize: BigInt(file.fileSize),
+                        md5: file.fileMd5,
+                        md5S2: file.fileMd5,
+                        checkKey: file.fileSha1,
+                        fileId: preRespData.upload?.uuid,
+                        uploadKey: preRespData.upload?.mediaPlatformUploadKey,
+                    },
+                    clientInfo: {
+                        clientType: 3,
+                        appId: "100",
+                        terminalType: 3,
+                        clientVer: "1.1.1",
+                        unknown: 4
+                    },
+                    fileNameInfo: {
+                        fileName: file.fileName
+                    },
+                    host: {
+                        hosts: [
+                            {
+                                url: {
+                                    host: preRespData.upload?.uploadIp,
+                                    unknown: 1,
+                                },
+                                port: preRespData.upload?.uploadPort,
+                            }
+                        ]
+                    }
+                },
+                unknown200: 1,
+                unknown3: 0
+            })
+            await this.packetHighwayClient.upload(
+                95,
+                fs.createReadStream(file.filePath, {highWaterMark: BlockSize}),
+                file.fileSize,
+                file.fileMd5,
+                ext
+            );
+        }
+        file.fileUuid = preRespData.upload?.uuid;
+        file.fileHash = preRespData.upload?.fileAddon;
+        const FetchExistFileReq = this.packer.packOfflineFileDownloadReq(file.fileUuid!, file.fileHash!, this.sig.uid, peerUid);
+        const resp = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0xe37_800', FetchExistFileReq, true);
+        console.log("OidbSvcTrpcTcp.0xe37_800", resp);
+        const oidb_resp = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(Buffer.from(resp.hex_data, 'hex'));
+        file._e37_800_rsp = new NapProtoMsg(OidbSvcTrpcTcp0XE37_800Response).decode(oidb_resp.body);
+        file._private_send_uid = this.sig.uid;
+        file._private_recv_uid = peerUid;
+    }
 }
diff --git a/src/core/packet/msg/builder.ts b/src/core/packet/msg/builder.ts
index 97736ae2..ea6a1d3c 100644
--- a/src/core/packet/msg/builder.ts
+++ b/src/core/packet/msg/builder.ts
@@ -2,7 +2,8 @@ import * as crypto from "crypto";
 import { PushMsgBody } from "@/core/packet/proto/message/message";
 import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto";
 import { LogWrapper } from "@/common/log";
-import { PacketMsg } from "@/core/packet/msg/message";
+import { PacketMsg, PacketSendMsgElement } from "@/core/packet/msg/message";
+import { IPacketMsgElement } from "@/core/packet/msg/element";
 
 export class PacketMsgBuilder {
     private logger: LogWrapper;
@@ -14,6 +15,9 @@ export class PacketMsgBuilder {
     buildFakeMsg(selfUid: string, element: PacketMsg[]): NapProtoEncodeStructType<typeof PushMsgBody>[] {
         return element.map((node): NapProtoEncodeStructType<typeof PushMsgBody> => {
             const avatar = `https://q.qlogo.cn/headimg_dl?dst_uin=${node.senderUin}&spec=640&img_type=jpg`;
+            const msgContent = node.msg.reduceRight((acc: undefined | Uint8Array, msg: IPacketMsgElement<PacketSendMsgElement>) => {
+                return acc !== undefined ? acc : msg.buildContent();
+            }, undefined);
             const msgElement = node.msg.flatMap(msg => msg.buildElement() ?? []);
             return {
                 responseHead: {
@@ -50,7 +54,8 @@ export class PacketMsgBuilder {
                 body: {
                     richText: {
                         elems: msgElement
-                    }
+                    },
+                    msgContent: msgContent,
                 }
             };
         });
diff --git a/src/core/packet/msg/element.ts b/src/core/packet/msg/element.ts
index e9693e48..e96a0c30 100644
--- a/src/core/packet/msg/element.ts
+++ b/src/core/packet/msg/element.ts
@@ -28,6 +28,8 @@ import {
 import {MsgInfo} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
 import {PacketMsg, PacketSendMsgElement} from "@/core/packet/msg/message";
 import {ForwardMsgBuilder} from "@/common/forward-msg-builder";
+import { FileExtra, GroupFileExtra } from "@/core/packet/proto/message/component";
+import { OidbSvcTrpcTcp0XE37_800Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
 
 // raw <-> packet
 // TODO: SendStructLongMsgElement
@@ -39,8 +41,8 @@ export abstract class IPacketMsgElement<T extends PacketSendMsgElement> {
         return undefined;
     }
 
-    buildElement(): NapProtoEncodeStructType<typeof Elem>[] | undefined {
-        return undefined;
+    buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
+        return [];
     }
 
     toPreview(): string {
@@ -351,8 +353,85 @@ export class PacketMsgPttElement extends IPacketMsgElement<SendPttElement> {
 }
 
 export class PacketMsgFileElement extends IPacketMsgElement<SendFileElement> {
+    fileName: string;
+    filePath: string;
+    fileSize: number;
+    fileSha1?: Uint8Array;
+    fileMd5?: Uint8Array;
+    fileUuid?: string;
+    fileHash?: string;
+    isGroupFile?: boolean;
+    _private_send_uid?: string;
+    _private_recv_uid?: string;
+    _e37_800_rsp?: NapProtoEncodeStructType<typeof OidbSvcTrpcTcp0XE37_800Response>
+
     constructor(element: SendFileElement) {
         super(element);
+        this.fileName = element.fileElement.fileName;
+        this.filePath = element.fileElement.filePath;
+        this.fileSize = +element.fileElement.fileSize;
+    }
+
+    buildContent(): Uint8Array | undefined {
+        if (this.isGroupFile) return undefined;
+        return new NapProtoMsg(FileExtra).encode({
+            file: {
+                fileType: 0,
+                fileUuid: this.fileUuid,
+                fileMd5: this.fileMd5,
+                fileName: this.fileName,
+                fileSize: BigInt(this.fileSize),
+                subcmd: 1,
+                dangerEvel: 0,
+                expireTime: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60,
+                fileHash: this.fileHash,
+            },
+            field6: {
+                field2: {
+                    field1: this._e37_800_rsp?.body?.field30?.field110,
+                    fileUuid: this.fileUuid,
+                    fileName: this.fileName,
+                    field6: this._e37_800_rsp?.body?.field30?.field3,
+                    field7: this._e37_800_rsp?.body?.field30?.field101,
+                    field8: this._e37_800_rsp?.body?.field30?.field100,
+                    timestamp1: this._e37_800_rsp?.body?.field30?.timestamp1,
+                    fileHash: this.fileHash,
+                    selfUid: this._private_send_uid,
+                    destUid: this._private_recv_uid,
+                }
+            }
+        })
+    }
+
+    buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
+        if (!this.isGroupFile) return [];
+        const lb = Buffer.alloc(2);
+        const transElemVal = new NapProtoMsg(GroupFileExtra).encode({
+            field1: 6,
+            fileName: this.fileName,
+            inner: {
+                info: {
+                    busId: 102,
+                    fileId: this.fileUuid,
+                    fileSize: BigInt(this.fileSize),
+                    fileName: this.fileName,
+                    fileSha: this.fileSha1,
+                    extInfoString: "",
+                    fileMd5: this.fileMd5,
+                }
+            }
+        })
+        lb.writeUInt16BE(transElemVal.length);
+        return [{
+            transElem: {
+                elemType: 24,
+                elemValue: Buffer.concat([Buffer.from([0x01]), lb, transElemVal]) // TLV
+            }
+        }];
+    }
+
+    toPreview(): string {
+        return `[文件]${this.fileName}`;
     }
 }
 
diff --git a/src/core/packet/packer.ts b/src/core/packet/packer.ts
index b3f8dd74..8b7d3277 100644
--- a/src/core/packet/packer.ts
+++ b/src/core/packet/packer.ts
@@ -1,6 +1,6 @@
 import * as zlib from "node:zlib";
 import * as crypto from "node:crypto";
-import { calculateSha1 } from "@/core/packet/utils/crypto/hash";
+import { calculateSha1, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
 import { NapProtoMsg } from "@/core/packet/proto/NapProto";
 import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase";
 import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202";
@@ -11,13 +11,20 @@ import { NTV2RichMediaReq } from "@/core/packet/proto/oidb/common/Ntv2.RichMedia
 import { HttpConn0x6ff_501 } from "@/core/packet/proto/action/action";
 import { LongMsgResult, SendLongMsgReq } from "@/core/packet/proto/message/action";
 import { PacketMsgBuilder } from "@/core/packet/msg/builder";
-import { PacketMsgPicElement, PacketMsgPttElement, PacketMsgVideoElement } from "@/core/packet/msg/element";
+import {
+    PacketMsgFileElement,
+    PacketMsgPicElement,
+    PacketMsgPttElement,
+    PacketMsgVideoElement
+} from "@/core/packet/msg/element";
 import { LogWrapper } from "@/common/log";
 import { PacketMsg } from "@/core/packet/msg/message";
 import { OidbSvcTrpcTcp0x6D6 } from "@/core/packet/proto/oidb/Oidb.0x6D6";
 import { OidbSvcTrpcTcp0XE37_1200 } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
 import { PacketMsgConverter } from "@/core/packet/msg/converter";
 import { PacketClient } from "@/core/packet/client";
+import { OidbSvcTrpcTcp0XE37_1700 } from "@/core/packet/proto/oidb/Oidb.0xE37_1700";
+import { OidbSvcTrpcTcp0XE37_800 } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
 
 export type PacketHexStr = string & { readonly hexNya: unique symbol };
 
@@ -596,6 +603,65 @@ export class PacketPacker {
         return this.toHexStr(this.packOidbPacket(0x126D, 100, req, true, false));
     }
 
+    async packUploadGroupFileReq(groupUin: number, file: PacketMsgFileElement): Promise<PacketHexStr> {
+        const body = new NapProtoMsg(OidbSvcTrpcTcp0x6D6).encode({
+            file: {
+                groupUin: groupUin,
+                appId: 4,
+                busId: 102,
+                entrance: 6,
+                targetDirectory: '/',  // TODO:
+                fileName: file.fileName,
+                localDirectory: `/${file.fileName}`,
+                fileSize: BigInt(file.fileSize),
+                fileMd5: file.fileMd5,
+                fileSha1: await calculateSha1(file.filePath),
+                fileSha3: Buffer.alloc(0),
+                field15: true
+            }
+        });
+        return this.toHexStr(this.packOidbPacket(0x6D6, 0, body, true, false));
+    }
+
+    async packUploadC2CFileReq(selfUid: string, peerUid: string, file: PacketMsgFileElement): Promise<PacketHexStr> {
+        const body = new NapProtoMsg(OidbSvcTrpcTcp0XE37_1700).encode({
+            command: 1700,
+            seq: 0,
+            upload: {
+                senderUid: selfUid,
+                receiverUid: peerUid,
+                fileSize: file.fileSize,
+                fileName: file.fileName,
+                md510MCheckSum: await computeMd5AndLengthWithLimit(file.filePath, 10*1024*1024),
+                sha1CheckSum: file.fileSha1,
+                localPath: "/",
+                md5CheckSum: file.fileMd5,
+                sha3CheckSum: Buffer.alloc(0)
+            },
+            businessId: 3,
+            clientType: 1,
+            flagSupportMediaPlatform: 1
+        })
+        return this.toHexStr(this.packOidbPacket(0xE37, 1700, body, false, false));
+    }
+
+    packOfflineFileDownloadReq(fileUUID: string, fileHash: string, senderUid: string, receiverUid: string): PacketHexStr {
+        const req = this.packOidbPacket(0xE37, 800, new NapProtoMsg(OidbSvcTrpcTcp0XE37_800).encode({
+            subCommand: 800,
+            field2: 0,
+            body: {
+                senderUid: senderUid,
+                receiverUid: receiverUid,
+                fileUuid: fileUUID,
+                fileHash: fileHash,
+            },
+            field101: 3,
+            field102: 1,
+            field200: 1,
+        }), false, false);
+        return this.toHexStr(req);
+    }
+
     packGroupFileDownloadReq(groupUin: number, fileUUID: string): PacketHexStr {
         return this.toHexStr(
             this.packOidbPacket(0x6D6, 2, new NapProtoMsg(OidbSvcTrpcTcp0x6D6).encode({
diff --git a/src/core/packet/proto/message/component.ts b/src/core/packet/proto/message/component.ts
index 984b5f3a..c83ed1e9 100644
--- a/src/core/packet/proto/message/component.ts
+++ b/src/core/packet/proto/message/component.ts
@@ -113,8 +113,26 @@ export const Permission = {
 
 export const FileExtra = {
     file: ProtoField(1, () => NotOnlineFile),
+    field6: ProtoField(6, () => PrivateFileExtra),
 };
 
+export const PrivateFileExtra = {
+    field2: ProtoField(2, () => PrivateFileExtraField2),
+}
+
+export const PrivateFileExtraField2 = {
+    field1: ProtoField(1, ScalarType.UINT32),
+    fileUuid: ProtoField(4, ScalarType.STRING),
+    fileName: ProtoField(5, ScalarType.STRING),
+    field6: ProtoField(6, ScalarType.UINT32),
+    field7: ProtoField(7, ScalarType.BYTES),
+    field8: ProtoField(8, ScalarType.BYTES),
+    timestamp1: ProtoField(9, ScalarType.UINT32),
+    fileHash: ProtoField(14, ScalarType.STRING),
+    selfUid: ProtoField(15, ScalarType.STRING),
+    destUid: ProtoField(16, ScalarType.STRING),
+}
+
 export const GroupFileExtra = {
     field1: ProtoField(1, ScalarType.UINT32),
     fileName: ProtoField(2, ScalarType.STRING),
@@ -132,8 +150,9 @@ export const GroupFileExtraInfo = {
     fileSize: ProtoField(3, ScalarType.UINT64),
     fileName: ProtoField(4, ScalarType.STRING),
     field5: ProtoField(5, ScalarType.UINT32),
-    field7: ProtoField(7, ScalarType.STRING),
-    fileMd5: ProtoField(8, ScalarType.STRING),
+    fileSha: ProtoField(6, ScalarType.BYTES),
+    extInfoString: ProtoField(7, ScalarType.STRING),
+    fileMd5: ProtoField(8, ScalarType.BYTES),
 };
 
 export const ImageExtraUrl = {
diff --git a/src/core/packet/proto/oidb/Oidb.0XE37_800.ts b/src/core/packet/proto/oidb/Oidb.0XE37_800.ts
new file mode 100644
index 00000000..f2e6fe5b
--- /dev/null
+++ b/src/core/packet/proto/oidb/Oidb.0XE37_800.ts
@@ -0,0 +1,62 @@
+import { ScalarType } from "@protobuf-ts/runtime";
+import { ProtoField } from "../NapProto";
+import {OidbSvcTrpcTcp0XE37_800_1200Metadata} from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
+
+export const OidbSvcTrpcTcp0XE37_800 = {
+    subCommand: ProtoField(1, ScalarType.UINT32),
+    field2: ProtoField(2, ScalarType.INT32),
+    body: ProtoField(10, () => OidbSvcTrpcTcp0XE37_800Body, true),
+    field101: ProtoField(101, ScalarType.INT32),
+    field102: ProtoField(102, ScalarType.INT32),
+    field200: ProtoField(200, ScalarType.INT32)
+};
+
+export const OidbSvcTrpcTcp0XE37_800Body = {
+    senderUid: ProtoField(10, ScalarType.STRING, true),
+    receiverUid: ProtoField(20, ScalarType.STRING, true),
+    fileUuid: ProtoField(30, ScalarType.STRING, true),
+    fileHash: ProtoField(40, ScalarType.STRING, true)
+};
+
+export const OidbSvcTrpcTcp0XE37Response = {
+    command: ProtoField(1, ScalarType.UINT32),
+    seq: ProtoField(2, ScalarType.INT32),
+    upload: ProtoField(19, () => ApplyUploadRespV3, true),
+    businessId: ProtoField(101, ScalarType.INT32),
+    clientType: ProtoField(102, ScalarType.INT32),
+    flagSupportMediaPlatform: ProtoField(200, ScalarType.INT32)
+};
+
+export const ApplyUploadRespV3 = {
+    retCode: ProtoField(10, ScalarType.INT32),
+    retMsg: ProtoField(20, ScalarType.STRING, true),
+    totalSpace: ProtoField(30, ScalarType.INT64),
+    usedSpace: ProtoField(40, ScalarType.INT64),
+    uploadedSize: ProtoField(50, ScalarType.INT64),
+    uploadIp: ProtoField(60, ScalarType.STRING, true),
+    uploadDomain: ProtoField(70, ScalarType.STRING, true),
+    uploadPort: ProtoField(80, ScalarType.UINT32),
+    uuid: ProtoField(90, ScalarType.STRING, true),
+    uploadKey: ProtoField(100, ScalarType.BYTES, true),
+    boolFileExist: ProtoField(110, ScalarType.BOOL),
+    packSize: ProtoField(120, ScalarType.INT32),
+    uploadIpList: ProtoField(130, ScalarType.STRING, false, true), // repeated
+    uploadHttpsPort: ProtoField(140, ScalarType.INT32),
+    uploadHttpsDomain: ProtoField(150, ScalarType.STRING, true),
+    uploadDns: ProtoField(160, ScalarType.STRING, true),
+    uploadLanip: ProtoField(170, ScalarType.STRING, true),
+    fileAddon: ProtoField(200, ScalarType.STRING, true),
+    mediaPlatformUploadKey: ProtoField(220, ScalarType.BYTES, true)
+};
+
+export const OidbSvcTrpcTcp0XE37_800Response = {
+    command: ProtoField(1, ScalarType.UINT32, true),
+    subCommand: ProtoField(2, ScalarType.UINT32, true),
+    body: ProtoField(10, () => OidbSvcTrpcTcp0XE37_800ResponseBody, true),
+    field50: ProtoField(50, ScalarType.UINT32, true),
+};
+
+export const OidbSvcTrpcTcp0XE37_800ResponseBody = {
+    field10: ProtoField(10, ScalarType.UINT32, true),
+    field30: ProtoField(30, () => OidbSvcTrpcTcp0XE37_800_1200Metadata, true),
+}
diff --git a/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts b/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts
index 80ecbe2c..ba2b0f16 100644
--- a/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts
+++ b/src/core/packet/proto/oidb/Oidb.0xE37_1200.ts
@@ -30,7 +30,7 @@ export const OidbSvcTrpcTcp0XE37_1200ResponseBody = {
     field10: ProtoField(10, ScalarType.UINT32, true),
     state: ProtoField(20, ScalarType.STRING, true),
     result: ProtoField(30, () => OidbSvcTrpcTcp0XE37_1200Result, true),
-    metadata: ProtoField(40, () => OidbSvcTrpcTcp0XE37_1200Metadata, true),
+    metadata: ProtoField(40, () => OidbSvcTrpcTcp0XE37_800_1200Metadata, true),
 };
 
 export const OidbSvcTrpcTcp0XE37_1200Result = {
@@ -43,7 +43,7 @@ export const OidbSvcTrpcTcp0XE37_1200Result = {
     extra: ProtoField(120, ScalarType.BYTES, true),
 };
 
-export const OidbSvcTrpcTcp0XE37_1200Metadata = {
+export const OidbSvcTrpcTcp0XE37_800_1200Metadata = {
     uin: ProtoField(1, ScalarType.UINT32, true),
     field2: ProtoField(2, ScalarType.UINT32, true),
     field3: ProtoField(3, ScalarType.UINT32, true),
diff --git a/src/core/packet/proto/oidb/Oidb.0xE37_1700.ts b/src/core/packet/proto/oidb/Oidb.0xE37_1700.ts
new file mode 100644
index 00000000..8d5c3f73
--- /dev/null
+++ b/src/core/packet/proto/oidb/Oidb.0xE37_1700.ts
@@ -0,0 +1,23 @@
+import { ScalarType } from "@protobuf-ts/runtime";
+import { ProtoField } from "../NapProto";
+
+export const OidbSvcTrpcTcp0XE37_1700 = {
+    command: ProtoField(1, ScalarType.UINT32, true),
+    seq: ProtoField(2, ScalarType.INT32, true),
+    upload: ProtoField(19, () => ApplyUploadReqV3, true),
+    businessId: ProtoField(101, ScalarType.INT32, true),
+    clientType: ProtoField(102, ScalarType.INT32, true),
+    flagSupportMediaPlatform: ProtoField(200, ScalarType.INT32, true),
+}
+
+export const ApplyUploadReqV3 = {
+    senderUid: ProtoField(10, ScalarType.STRING, true),
+    receiverUid: ProtoField(20, ScalarType.STRING, true),
+    fileSize: ProtoField(30, ScalarType.UINT32, true),
+    fileName: ProtoField(40, ScalarType.STRING, true),
+    md510MCheckSum: ProtoField(50, ScalarType.BYTES, true),
+    sha1CheckSum: ProtoField(60, ScalarType.BYTES, true),
+    localPath: ProtoField(70, ScalarType.STRING, true),
+    md5CheckSum: ProtoField(110, ScalarType.BYTES, true),
+    sha3CheckSum: ProtoField(120, ScalarType.BYTES, true),
+}
diff --git a/src/core/packet/utils/crypto/hash.ts b/src/core/packet/utils/crypto/hash.ts
index 51eaf4ec..9576e2c0 100644
--- a/src/core/packet/utils/crypto/hash.ts
+++ b/src/core/packet/utils/crypto/hash.ts
@@ -11,11 +11,23 @@ function sha1Stream(readable: stream.Readable) {
     }) as Promise<Buffer>;
 }
 
+function md5Stream(readable: stream.Readable) {
+    return new Promise((resolve, reject) => {
+        readable.on('error', reject);
+        readable.pipe(crypto.createHash('md5').on('error', reject).on('data', resolve));
+    }) as Promise<Buffer>;
+}
+
 export function calculateSha1(filePath: string): Promise<Buffer> {
     const readable = fs.createReadStream(filePath);
     return sha1Stream(readable);
 }
 
+export function computeMd5AndLengthWithLimit(filePath: string, limit?: number): Promise<Buffer> {
+    const readStream = fs.createReadStream(filePath, limit ? { start: 0, end: limit - 1 } : {});
+    return md5Stream(readStream);
+}
+
 export function calculateSha1StreamBytes(filePath: string): Promise<Buffer[]> {
     return new Promise((resolve, reject) => {
         const readable = fs.createReadStream(filePath);