Image download support, close #49

This commit is contained in:
Him188 2020-02-22 23:32:47 +08:00
parent bf1e75eb36
commit 692e7c950c
7 changed files with 75 additions and 85 deletions

View File

@ -9,7 +9,9 @@
package net.mamoe.mirai.qqandroid
import kotlinx.io.core.ByteReadPacket
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.utils.io.ByteReadChannel
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.BotImpl
import net.mamoe.mirai.contact.*
@ -17,10 +19,9 @@ import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.data.FriendInfo
import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.messageRandom
import net.mamoe.mirai.message.data.sequenceId
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.message.CustomFaceFromServer
import net.mamoe.mirai.qqandroid.message.NotOnlineImageFromServer
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
@ -150,13 +151,20 @@ internal abstract class QQAndroidBotBase constructor(
}
}
override suspend fun Image.download(): ByteReadPacket {
TODO("not implemented")
override suspend fun Image.url(): String = "http://gchat.qpic.cn" + when (this) {
is NotOnlineImageFromServer -> this.delegate.origUrl
is CustomFaceFromServer -> this.delegate.origUrl
is CustomFaceFromFile -> {
TODO()
}
is NotOnlineImageFromFile -> {
TODO()
}
else -> error("unsupported image class: ${this::class.simpleName}")
}
@Suppress("OverridingDeprecatedMember")
override suspend fun Image.downloadAsByteArray(): ByteArray {
TODO("not implemented")
override suspend fun Image.channel(): ByteReadChannel {
return Http.get<HttpResponse>(url()).content
}
override suspend fun approveFriendAddRequest(id: Long, remark: String?) {

View File

@ -11,14 +11,11 @@
package net.mamoe.mirai
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.io.OutputStream
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.use
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.data.FriendInfo
@ -30,7 +27,6 @@ import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.LoginFailedException
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.transferTo
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmStatic
@ -226,11 +222,12 @@ abstract class Bot : CoroutineScope {
abstract suspend fun recall(source: MessageSource)
/**
* 撤回这条消息.
* 撤回一条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
*
* [Bot] 撤回自己的消息不需要权限.
* [Bot] 撤回群员的消息需要管理员权限.
*
* @param senderId 这条消息的发送人. 可以为 [Bot.uin] [Member.id]
* @param messageId [MessageSource.id]
*
* @throws PermissionDeniedException [Bot] 无权限操作时
@ -239,15 +236,18 @@ abstract class Bot : CoroutineScope {
*/
abstract suspend fun recall(groupId: Long, senderId: Long, messageId: Long)
@Deprecated("内存使用效率十分低下", ReplaceWith("this.download()"), DeprecationLevel.WARNING)
@MiraiExperimentalAPI("未支持")
abstract suspend fun Image.downloadAsByteArray(): ByteArray
/**
* 获取图片下载链接
*/
abstract suspend fun Image.url(): String
/**
* 将图片下载到内存中 (使用 [IoBuffer.Pool])
* 获取图片下载链接并开始下载.
*
* @see ByteReadChannel.copyAndClose
* @see ByteReadChannel.copyTo
*/
@MiraiExperimentalAPI("未支持")
abstract suspend fun Image.download(): ByteReadPacket
abstract suspend fun Image.channel(): ByteReadChannel
/**
* 添加一个好友
@ -278,20 +278,7 @@ abstract class Bot : CoroutineScope {
*/
abstract fun close(cause: Throwable? = null)
// region extensions
final override fun toString(): String {
return "Bot(${uin})"
}
/**
* 需要调用者自行 close [output]
*/
@MiraiExperimentalAPI("未支持")
suspend inline fun Image.downloadTo(output: OutputStream) =
download().use { input -> input.transferTo(output) }
// endregion
final override fun toString(): String = "Bot(${uin})"
}
/**

View File

@ -11,9 +11,7 @@
package net.mamoe.mirai.message
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.readBytes
import io.ktor.utils.io.ByteReadChannel
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group
@ -128,20 +126,22 @@ abstract class MessagePacketBase<TSender : QQ, TSubject : Contact> : Packet, Bot
// endregion
// region 下载图片
/**
* 将图片下载到内存.
* 获取图片下载链接
*
* 非常不推荐这样做.
* @return "http://gchat.qpic.cn/gchatpic_new/..."
*/
@Deprecated("内存使用效率十分低下", ReplaceWith("this.download()"), DeprecationLevel.WARNING)
suspend inline fun Image.downloadAsByteArray(): ByteArray = bot.run { download().readBytes() }
// TODO: 2020/2/5 为下载图片添加文件系统的存储方式
suspend inline fun Image.url(): String = bot.run { url() }
/**
* 将图片下载到内存缓存中 (使用 [IoBuffer.Pool])
* 获取图片下载链接并开始下载.
*
* @see ByteReadChannel.copyAndClose
* @see ByteReadChannel.copyTo
*/
suspend inline fun Image.download(): ByteReadPacket = bot.run { download() }
suspend inline fun Image.channel(): ByteReadChannel = bot.run { channel() }
// endregion
}

View File

@ -16,7 +16,6 @@ package net.mamoe.mirai.utils
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.readAvailable
import kotlinx.coroutines.io.close
import kotlinx.io.OutputStream
import kotlinx.io.core.Output
import kotlinx.io.pool.useInstance
@ -24,6 +23,7 @@ import net.mamoe.mirai.utils.io.ByteArrayPool
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
// copyTo
/**
* 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
@ -49,6 +49,8 @@ suspend fun ByteReadChannel.copyTo(dst: Output) {
}
}
/* // 垃圾 kotlin, Unresolved reference: ByteWriteChannel
/**
* 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
*/
@ -60,7 +62,7 @@ suspend fun ByteReadChannel.copyTo(dst: kotlinx.coroutines.io.ByteWriteChannel)
} while (size != 0)
}
}
*/
// copyAndClose
@ -97,18 +99,19 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) {
}
}
/*// 垃圾 kotlin, Unresolved reference: ByteWriteChannel
/**
* 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
*/
suspend fun ByteReadChannel.copyAndClose(dst: kotlinx.coroutines.io.ByteWriteChannel) {
try {
dst.close(kotlin.runCatching {
ByteArrayPool.useInstance {
do {
val size = this.readAvailable(it)
dst.writeFully(it, 0, size)
} while (size != 0)
}
} finally {
dst.close()
}
}
}.exceptionOrNull())
}
*/

View File

@ -11,23 +11,19 @@
package net.mamoe.mirai.message
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.io.core.Input
import kotlinx.io.core.use
import kotlinx.io.streams.inputStream
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.toExternalImage
import net.mamoe.mirai.utils.copyAndClose
import net.mamoe.mirai.utils.copyTo
import java.awt.image.BufferedImage
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.net.URL
import javax.imageio.ImageIO
/**
* 一条从服务器接收到的消息事件.
@ -72,16 +68,22 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
// endregion 发送图片 (扩展)
// region 下载图片 (扩展)
suspend inline fun Image.downloadTo(file: File): Long = file.outputStream().use { downloadTo(it) }
suspend inline fun Image.downloadTo(file: File) = file.outputStream().use { downloadTo(it) }
/**
* 这个函数结束后不会关闭 [output]. 请务必解决好 [OutputStream.close]
* 下载图片到 [output] 但不关闭这个 [output]
*/
suspend inline fun Image.downloadTo(output: OutputStream): Long =
download().inputStream().use { input -> withContext(Dispatchers.IO) { input.copyTo(output) } }
suspend inline fun Image.downloadTo(output: OutputStream) = channel().copyTo(output)
suspend inline fun Image.downloadAsStream(): InputStream = download().inputStream()
suspend inline fun Image.downloadAsExternalImage(): ExternalImage = withContext(Dispatchers.IO) { download().toExternalImage() }
/**
* 下载图片到 [output] 并关闭这个 [output]
*/
suspend inline fun Image.downloadAndClose(output: OutputStream) = channel().copyAndClose(output)
/*
suspend inline fun Image.downloadAsStream(): InputStream = channel().asInputStream()
suspend inline fun Image.downloadAsExternalImage(): ExternalImage = withContext(Dispatchers.IO) { downloadAsStream().toExternalImage() }
suspend inline fun Image.downloadAsBufferedImage(): BufferedImage = withContext(Dispatchers.IO) { ImageIO.read(downloadAsStream()) }
*/
// endregion
}

View File

@ -1,6 +1,5 @@
package net.mamoe.mirai.japt;
import kotlinx.io.core.ByteReadPacket;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.BotAccount;
import net.mamoe.mirai.BotFactoryJvmKt;
@ -153,18 +152,16 @@ public interface BlockingBot {
// region actions
@NotNull
byte[] downloadAsByteArray(@NotNull Image image);
@NotNull
ByteReadPacket download(@NotNull Image image);
/**
* 下载图片到 {@code outputStream}.
* <p>
* 不会自动关闭 {@code outputStream}
*/
void download(@NotNull Image image, @NotNull OutputStream outputStream);
void downloadTo(@NotNull Image image, @NotNull OutputStream outputStream);
/**
* 下载图片到 {@code outputStream} 并关闭 stream
*/
void downloadAndClose(@NotNull Image image, @NotNull OutputStream outputStream);
/**
* 添加一个好友

View File

@ -10,8 +10,6 @@
package net.mamoe.mirai.japt.internal
import kotlinx.coroutines.runBlocking
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.contact.QQ
@ -23,10 +21,7 @@ import net.mamoe.mirai.japt.BlockingGroup
import net.mamoe.mirai.japt.BlockingQQ
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.toList
import net.mamoe.mirai.utils.*
import java.io.OutputStream
import java.util.stream.Stream
import kotlin.streams.asStream
@ -59,10 +54,8 @@ internal class BlockingBotImpl(private val bot: Bot) : BlockingBot {
override fun getGroup(id: Long): BlockingGroup = runBlocking { bot.getGroup(id).blocking() }
override fun getNetwork(): BotNetworkHandler = bot.network
override fun login() = runBlocking { bot.login() }
override fun downloadAsByteArray(image: Image): ByteArray = bot.run { runBlocking { image.download().readBytes() } }
override fun download(image: Image): ByteReadPacket = bot.run { runBlocking { image.download() } }
override fun download(image: Image, outputStream: OutputStream) = bot.run { runBlocking { image.downloadTo(outputStream) } }
override fun downloadTo(image: Image, outputStream: OutputStream) = bot.run { runBlocking { image.channel().copyTo(outputStream) } }
override fun downloadAndClose(image: Image, outputStream: OutputStream) = bot.run { runBlocking { image.channel().copyAndClose(outputStream) } }
override fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult = runBlocking { bot.addFriend(id, message, remark) }
override fun approveFriendAddRequest(id: Long, remark: String?) = runBlocking { bot.approveFriendAddRequest(id, remark) }
override fun close(throwable: Throwable?) = bot.close(throwable)