diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
index e13db5f34..c5e4fd723 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
@@ -51,7 +51,7 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
         }
     }
 
-    override suspend fun uploadImage(image: ExternalImage): Image {
+    override suspend fun uploadImage(image: ExternalImage): Image = try {
         bot.network.run {
             val response = LongConn.OffPicUp(
                 bot.client, Cmd0x352.TryUpImgReq(
@@ -60,12 +60,11 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
                     fileId = 0,
                     fileMd5 = image.md5,
                     fileSize = image.inputSize.toInt(),
-                    fileName = image.md5.toUHexString("") + ".jpg",
+                    fileName = image.md5.toUHexString("") + "." + image.format,
                     imgOriginal = 1,
                     imgWidth = image.width,
                     imgHeight = image.height,
-                    imgType = image.imageType,
-                    buType = 0
+                    imgType = image.imageType
                 )
             ).sendAndExpect<LongConn.OffPicUp.Response>()
 
@@ -81,7 +80,6 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
                 is LongConn.OffPicUp.Response.RequireUpload -> {
                     HighwayHelper.uploadImage(
                         client = bot.client,
-                        uin = bot.uin,
                         serverIp = response.serverIp[0].toIpV4AddressString(),
                         serverPort = response.serverPort[0],
                         imageInput = image.input,
@@ -103,6 +101,8 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
                 is LongConn.OffPicUp.Response.Failed -> error(response.message)
             }
         }
+    } finally {
+        image.input.close()
     }
 
     override suspend fun queryProfile(): Profile {
@@ -355,7 +355,7 @@ internal class GroupImpl(
         }
     }
 
-    override suspend fun uploadImage(image: ExternalImage): Image {
+    override suspend fun uploadImage(image: ExternalImage): Image = try {
         bot.network.run {
             val response: ImgStore.GroupPicUp.Response = ImgStore.GroupPicUp(
                 bot.client,
@@ -393,7 +393,6 @@ internal class GroupImpl(
 
                     HighwayHelper.uploadImage(
                         client = bot.client,
-                        uin = bot.uin,
                         serverIp = response.uploadIpList.first().toIpV4AddressString(),
                         serverPort = response.uploadPortList.first(),
                         imageInput = image.input,
@@ -436,6 +435,8 @@ internal class GroupImpl(
                 }
             }
         }
+    } finally {
+        image.input.close()
     }
 
     override fun equals(other: Any?): Boolean {
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
index 074118e57..28bbc4527 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
@@ -104,8 +104,11 @@ internal open class QQAndroidClient(
     private val requestPacketRequestId: AtomicInt = atomic(1921334513)
     internal fun nextRequestPacketRequestId(): Int = requestPacketRequestId.getAndAdd(2)
 
-    private val highwayDataTransSequenceId: AtomicInt = atomic(87017)
-    internal fun nextHighwayDataTransSequenceId(): Int = highwayDataTransSequenceId.getAndAdd(2)
+    private val highwayDataTransSequenceIdForGroup: AtomicInt = atomic(87017)
+    internal fun nextHighwayDataTransSequenceIdForGroup(): Int = highwayDataTransSequenceIdForGroup.getAndAdd(2)
+
+    private val highwayDataTransSequenceIdForFriend: AtomicInt = atomic(40717)
+    internal fun nextHighwayDataTransSequenceIdForFriend(): Int = highwayDataTransSequenceIdForFriend.getAndAdd(2)
 
     val appClientVersion: Int = 0
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt
index 9478d2721..2e0649d07 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt
@@ -78,6 +78,9 @@ object Highway {
         md5: ByteArray
     ): ByteReadPacket {
         require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
+        require(data !is ByteReadPacket || data.remaining.toInt() == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as ByteReadPacket).remaining}" }
+        require(data !is IoBuffer || data.readRemaining == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as IoBuffer).readRemaining}" }
+
         val dataHighwayHead = CSDataHighwayHead.DataHighwayHead(
             version = 1,
             uin = uin.toString(),
@@ -91,7 +94,7 @@ object Highway {
         )
         val segHead = CSDataHighwayHead.SegHead(
             datalength = dataSize,
-            filesize = dataSize.toLong() and 0xFFffFFff,
+            filesize = dataSize.toLong(),
             serviceticket = uKey,
             md5 = md5,
             fileMd5 = md5,
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
index 55b52992a..bcd7fe054 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
@@ -15,7 +15,6 @@ internal object HighwayHelper {
 
     suspend fun uploadImage(
         client: QQAndroidClient,
-        uin: Long,
         serverIp: String,
         serverPort: Int,
         uKey: ByteArray,
@@ -26,14 +25,18 @@ internal object HighwayHelper {
     ) {
         require(md5.size == 16) { "bad md5. Required size=16, got ${md5.size}" }
         require(uKey.size == 128) { "bad uKey. Required size=128, got ${uKey.size}" }
+        require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
+
         val socket = PlatformSocket()
         socket.connect(serverIp, serverPort)
         socket.use {
             socket.send(
                 Highway.RequestDataTrans(
-                    uin = uin,
+                    uin = client.uin,
                     command = "PicUp.DataUp",
-                    sequenceId = client.nextHighwayDataTransSequenceId(),
+                    sequenceId =
+                    if (commandId == 2) client.nextHighwayDataTransSequenceIdForGroup()
+                    else client.nextHighwayDataTransSequenceIdForFriend(),
                     uKey = uKey,
                     data = imageInput,
                     dataSize = inputSize,
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x352.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x352.kt
index a45c6cd3e..a313b3942 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x352.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x352.kt
@@ -91,7 +91,7 @@ internal class Cmd0x352 : ProtoBuf {
         @SerialId(2) val msgTryupImgReq: List<TryUpImgReq>? = null,// optional
         @SerialId(3) val msgGetimgUrlReq: List<GetImgUrlReq>? = null,// optional
         @SerialId(4) val msgDelImgReq: List<DelImgReq>? = null,
-        @SerialId(10) val netType: Int = 0// 数据网络=5
+        @SerialId(10) val netType: Int = 3// 数据网络=5
     ) : ProtoBuf
 
     @Serializable
@@ -117,11 +117,10 @@ internal class Cmd0x352 : ProtoBuf {
         @SerialId(9) val innerIP: Int = 0,
         @SerialId(10) val addressBook: Int = 0,//chatType == 1006为1 我觉得发0没问题
         @SerialId(11) val retry: Int = 0,//default
-        @SerialId(12) val buType: Int,//1或96 不确定
+        @SerialId(12) val buType: Int = 1,//1或96 不确定
         @SerialId(13) val imgOriginal: Int,//是否为原图
         @SerialId(14) val imgWidth: Int,
         @SerialId(15) val imgHeight: Int,
-        @SerialId(16) val imgType: Int = 1000,
         /**
          * ImgType:
          *  JPG:    1000
@@ -131,7 +130,8 @@ internal class Cmd0x352 : ProtoBuf {
          *  GIG:    2000
          *  APNG:   2001
          *  SHARPP: 1004
-         * */
+         */
+        @SerialId(16) val imgType: Int = 1000,
         @SerialId(17) val buildVer: String = "8.2.0.1296",//版本号
         @SerialId(18) val fileIndex: ByteArray = EMPTY_BYTE_ARRAY,//default
         @SerialId(19) val fileStoreDays: Int = 0,//default
diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index 22b20102d..b8b22ddbb 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -85,7 +85,7 @@ kotlin {
                 api(ktor("network", ktorVersion))
                 //implementation("io.ktor:ktor-io:1.3.0-beta-1")
 
-                runtimeOnly(files("build/classes/kotlin/metadata/main")) // classpath is not properly set by IDE
+                //runtimeOnly(files("build/classes/kotlin/metadata/main")) // classpath is not properly set by IDE
             }
         }
         commonTest {
@@ -93,7 +93,7 @@ kotlin {
                 api(kotlin("test-annotations-common"))
                 api(kotlin("test-common"))
 
-                runtimeOnly(files("build/classes/kotlin/metadata/test")) // classpath is not properly set by IDE
+                //runtimeOnly(files("build/classes/kotlin/metadata/test")) // classpath is not properly set by IDE
             }
         }
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
index 8cbbea0a7..7a0e2d5f3 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
@@ -43,13 +43,13 @@ class ExternalImage(
         ): ExternalImage = ExternalImage(width, height, md5, format, data, data.remaining, filename)
     }
 
-    private val format: String = when (val it = imageFormat.toLowerCase()) {
-        "jpeg" -> "jpg" //必须转换
-        else -> it
-    }
+    val format: String =
+        when (val it = imageFormat.toLowerCase()) {
+            "jpeg" -> "jpg" //必须转换
+            else -> it
+        }
 
     /**
-     *
      * ImgType:
      *  JPG:    1000
      *  PNG:    1001