Okio的使用简介

news/2024/11/7 9:26:30/

Okio的使用简介

.

简介

Okio 是由square公司开发的用于IO读取。补充了Java.iojava.nio的不足,以便能够更加方便,快速的访问、存储和处理数据。内部的读写操作是在内存中进行的。是OkHttp的底层IO库。

.

Okio的核心类

  • ByteStrings: 是不可变的字节序列。它会自动将自己编码和解码为十六进制、base64utf-8

  • Buffers 是一个可变的字节序列。像Arraylist一样,你不需要预先设置缓冲区的大小。你可以将缓冲区读写为一个队列:将数据写到队尾,然后从队头读取。

  • Source: 类似于java的Inputstream(输入流),但也有所区别。

  • Sink 类似于java的Outputstream(输入流),但也有所区别。

.

Okio的流与Java的区别 :

  • 超时(Timeouts): 流提供了对底层I/O超时机制的访问。与java.io的socket字流不同,read()write()方法都给予超时机制。

  • 易于实施: source 只声明了三个方法:read()close()timeout()。没有像available()或单字节读取这样会导致正确性和性能意外的危险操作。

  • 使用方便: 虽然sourcesink的实现只有三种方法可写,但是调用方可以实现BufferedsourceBufferedsink接口, 这两个接口提供了丰富API能够满足你所需的一切。

  • 字节流和字符流之间没有人为的区别: 都是数据。你可以以字节、UTF-8字符串、big-endian的32位整数、little-endian的短整数等任何你想要的形式进行读写;不再有InputStreamReader!

  • 易于测试: Buffer类同时实现了BufferedSource和BufferedSink接口,因此测试代码简单明了。

.

.

添加的Okio的依赖

在项目的 build.gradle 中添加下面的依赖:

dependencies {implementation("com.squareup.okio:okio:2.10.0")
}

.

.

Okio的使用

.

1. 将数据写到文件中

@Throws(IOException::class)
fun writeFile(file: File) {file.sink().buffer().use { sink ->//循环的向文件中写入数据for ((key, value) in System.getenv()) {//数据格式:`key = value + 换行`sink.writeUtf8(key)sink.writeUtf8("=")sink.writeUtf8(value)sink.writeUtf8(system.lineeseseparator())//等价于(但前者性能更优)//因为VM不必创建和回收临时字符串//sink.writeUtf8(key + "=" + value + "\n"); }sink.close()}
}//在main()中调用方法来向文件中写入数据
fun main() {var file:File = File("文件路径")if!file.exists(){try {file.createNewFile();} catch (IOException e) {e.printStackTrace();}}//向文件中写入数据writeFile(file)
}

注:
在进行数据的写入的时候 sink.writeUtf8()方法只会在一行上输入不会自动换行,所以需要我们手动插入换行符;在大多数的程序都是所以"\n"来作为换行符的。在极少数情况下,你可以使用system.lineeseseparator()来代替"\n";它在Windows上返回"\r\n",在其他操作系统上返回"\n"

.

2. 逐行读取文件

@Throws(IOException::class)
fun readLinesFile(file: File) {file.source().use { fileSource ->fileSource.buffer().use { bufferedFileSource ->while (true) {//读取文件的内容val line = bufferedFileSource.readUtf8Line() ?: break//将文件的内容打印到控制台println(line)			}bufferedFileSource.close()}}
}//在main()中调用方法来读取指定的文件
fun main() {var file:File = File("文件路径")if!file.exists(){return;}//读取已经readLinesFile(file)
}

注:
使用 readUtf8Line()方法来读取文件的所有数据,在读取数据的时候,直到读取下一个数据为\n\r\n或文件的结尾前。它都以字符串的形式返回读取到的数据,并在最后省略定界符。当遇到空行时,该方法将返回一个空字符串。如果文件读取完成,将返回null。

.

3. 把二进制数据写入文件中

下面的示例代码是按照 BMP文件格式 对文件进行编码:

  //提供给外部调用的写入方法@Throws(IOException::class)fun writeBitmap(bitmap: Bitmap, file: File) {file.sink().buffer().use { sink -> encode(bitmap, sink) }}@Throws(IOException::class)fun encode(bitmap: Bitmap, sink: BufferedSink) {val height = bitmap.heightval width = bitmap.widthval bytesPerPixel = 3val rowByteCountWithoutPadding = bytesPerPixel * widthval rowByteCount = (rowByteCountWithoutPadding + 3) / 4 * 4val pixelDataSize = rowByteCount * heightval bmpHeaderSize = 14val dibHeaderSize = 40// BMP Headersink.writeUtf8("BM") // ID.sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize) // File size.sink.writeShortLe(0) // Unused.sink.writeShortLe(0) // Unused.sink.writeIntLe(bmpHeaderSize + dibHeaderSize) // Offset of pixel data.// DIB Headersink.writeIntLe(dibHeaderSize)sink.writeIntLe(width)sink.writeIntLe(height)sink.writeShortLe(1) // Color plane count.sink.writeShortLe(bytesPerPixel * Byte.SIZE_BITS)sink.writeIntLe(0) // No compression.sink.writeIntLe(16) // Size of bitmap data including padding.sink.writeIntLe(2835) // Horizontal print resolution in pixels/meter. (72 dpi).sink.writeIntLe(2835) // Vertical print resolution in pixels/meter. (72 dpi).sink.writeIntLe(0) // Palette color count.sink.writeIntLe(0) // 0 important colors.// Pixel data.for (y in height - 1 downTo 0) {for (x in 0 until width) {sink.writeByte(bitmap.blue(x, y))sink.writeByte(bitmap.green(x, y))sink.writeByte(bitmap.red(x, y))}// Padding for 4-byte alignment.for (p in rowByteCountWithoutPadding until rowByteCount) {sink.writeByte(0)}
}fun main() {val bitmap = Bitmap数据var file:File = File("文件路径")if!file.exists(){try {file.createNewFile();} catch (IOException e) {e.printStackTrace();}}writeBitmap(bitmap, file)
}

说明:

代码中对文件按照BMP的格式写入二进制数据,这会生成一个bmp格式的图片文件,BMP格式要求每行以4字节开始,所以代码中加了很多0来做字节对齐。

.

.

使用Okio进行Socket通信

下面代码是基于Okio来实现的本地Socket客户端,并与服务器的Socket进行通信。

//Socket服务的代理类
class SocksProxyServer {//创建线程池private val executor = Executors.newCachedThreadPool()//创建Socket服务private lateinit var serverSocket: ServerSocket//创建Set集合private val openSockets: MutableSet<Socket> = Collections.newSetFromMap(ConcurrentHashMap())//启动Socket服务@Throws(IOException::class)fun start() {serverSocket = ServerSocket(0)executor.execute { acceptSockets() }}//关闭Socket服务@Throws(IOException::class)fun shutdown() {serverSocket.close()executor.shutdown()}fun proxy(): Proxy = Proxy(Proxy.Type.SOCKS,InetSocketAddress.createUnresolved("localhost", serverSocket.localPort))//连接Socket远程服务private fun acceptSockets() {try {while (true) {val from = serverSocket.accept()openSockets.add(from)executor.execute { handleSocket(from) }}} catch (e: IOException) {println("shutting down: $e")} finally {for (socket in openSockets) {socket.close()}}}//连接Socket服务private fun handleSocket(fromSocket: Socket) {try {val fromSource = fromSocket.source().buffer()val fromSink = fromSocket.sink().buffer()// Read the hello.val socksVersion = fromSource.readByte().toInt() and 0xffif (socksVersion != VERSION_5) throw ProtocolException()val methodCount = fromSource.readByte().toInt() and 0xffvar foundSupportedMethod = falsefor (i in 0 until methodCount) {val method: Int = fromSource.readByte().toInt() and 0xfffoundSupportedMethod = foundSupportedMethod or (method == METHOD_NO_AUTHENTICATION_REQUIRED)}if (!foundSupportedMethod) throw ProtocolException()// Respond to hello.fromSink.writeByte(VERSION_5).writeByte(METHOD_NO_AUTHENTICATION_REQUIRED).emit()// Read a command.val version = fromSource.readByte().toInt() and 0xffval command = fromSource.readByte().toInt() and 0xffval reserved = fromSource.readByte().toInt() and 0xffif (version != VERSION_5 || command != COMMAND_CONNECT || reserved != 0) {throw ProtocolException()}// Read an address.val addressType = fromSource.readByte().toInt() and 0xffval inetAddress = when (addressType) {ADDRESS_TYPE_IPV4 -> InetAddress.getByAddress(fromSource.readByteArray(4L))ADDRESS_TYPE_DOMAIN_NAME -> {val domainNameLength: Int = fromSource.readByte().toInt() and 0xffInetAddress.getByName(fromSource.readUtf8(domainNameLength.toLong()))}else -> throw ProtocolException()}val port = fromSource.readShort().toInt() and 0xffff// Connect to the caller's specified host.val toSocket = Socket(inetAddress, port)openSockets.add(toSocket)val localAddress = toSocket.localAddress.addressif (localAddress.size != 4) throw ProtocolException()// Write the reply.fromSink.writeByte(VERSION_5).writeByte(REPLY_SUCCEEDED).writeByte(0).writeByte(ADDRESS_TYPE_IPV4).write(localAddress).writeShort(toSocket.localPort).emit()// Connect sources to sinks in both directions.val toSink = toSocket.sink()executor.execute { transfer(fromSocket, fromSource, toSink) }val toSource = toSocket.source()executor.execute { transfer(toSocket, toSource, fromSink) }} catch (e: IOException) {fromSocket.close()openSockets.remove(fromSocket)println("connect failed for $fromSocket: $e")}}/***从source读取数据并将其写入sink*/private fun transfer(sourceSocket: Socket, source: Source, sink: Sink) {try {val buffer = Buffer()var byteCount: Longwhile (source.read(buffer, 8192L).also { byteCount = it } != -1L) {sink.write(buffer, byteCount)sink.flush()}} catch (e: IOException) {println("transfer failed from $sourceSocket: $e")} finally {sink.close()source.close()sourceSocket.close()openSockets.remove(sourceSocket)}}
}fun main() {//定义Socket代理对象并初始化val proxyServer = SocksProxyServer()//启动Socket服务proxyServer.start()//连接Socket远程服务器val url = URL("https://publicobject.com/helloworld.txt")val connection = url.openConnection(proxyServer.proxy())//接收Socket远程服务器返回的数据connection.getInputStream().source().buffer().use { source ->while (true) {//读取文件的内容val line = source.readUtf8Line() ?: break//将文件的内容打印到控制台println(line)			}source.close()}//关闭Socket服务proxyServer.shutdown()
}

说明:

通过上述代码就可以实现与远程服务器的Socket进行通信,完成客户端与服务器的长链接通信。

.

.

Hashing(哈希散列)

Okio 可以对字节字符串Buffersource输入流sink输出流等进行哈希加密。

下面介绍Okio支持加密哈希函数:

  • MD5: 128位(16字节)加密哈希。它既不安全又是过时的,因为它的逆向成本很低!之所以提供此哈希,是因为它在安全性较低的系统中使用比较非常流行并且方便。

  • SHA-1: 160位(20字节)加密散列。最近的研究表明,创建SHA-1碰撞是可行的。考虑从sha-1升级到sha-256。

  • SHA-256: 256位(32字节)加密哈希。SHA-256被广泛理解,逆向操作成本较高。这是大多数系统应该使用的哈希。

  • SHA-512: 512位(64字节)加密哈希。逆向操作成本很高。

1. 对字节字符串进行加密

println("ByteString")
val byteString = readByteString(File("文件路径"))
println("       md5: " + byteString.md5().hex())
println("      sha1: " + byteString.sha1().hex())
println("    sha256: " + byteString.sha256().hex())
println("    sha512: " + byteString.sha512().hex())@Throws(IOException::class)fun readByteString(file: File): ByteString {return file.source().buffer().use { it.readByteString() }}

2. 对Buffer进行加密

println("Buffer")
val buffer = readBuffer(File("文件路径"))
println("       md5: " + buffer.md5().hex())
println("      sha1: " + buffer.sha1().hex())
println("    sha256: " + buffer.sha256().hex())
println("    sha512: " + buffer.sha512().hex())@Throws(IOException::class)fun readBuffer(file: File): Buffer {return file.source().use { source ->Buffer().also { it.writeAll(source) }}}

3. 对source输入流进行加密

println("HashingSource")
sha256(File("文件路径").source()).use { hashingSource ->hashingSource.buffer().use { source ->source.readAll(blackholeSink())println("    sha256: " + hashingSource.hash.hex())}
}

4. 对sink输出流进行加密

println("HashingSink")
sha256(blackholeSink()).use { hashingSink ->hashingSink.buffer().use { sink ->File("文件路径").source().use { source ->sink.writeAll(source)sink.close() // Emit anything buffered.println("    sha256: " + hashingSink.hash.hex())}}
}

5. 支持秘钥和hash值加密

println("HMAC")
val byteString = readByteString(File("文件路径"))
val secret = "7065616e7574627574746572".decodeHex()秘钥
println("hmacSha256: " + byteString.hmacSha256(secret).hex())

补充:

你可以从ByteString, Buffer, HashingSource, 和HashingSink中生成HMAC,但Okio没有为MD5实现HMAC。

.

.

加密和解密

Okio可以通过使用使用Okio.cipherSink(Sink,Cipher)Okio.cipherSource(Source,Cipher)让区块加密算法对Stream进行加密或解密。

以下示例显示了AES加密的典型用法,其中key和iv参数都应为16个字节长度:

Sink输入流进行加密

fun encryptAes(bytes: ByteString, file: File, key: ByteArray, iv: ByteArray) {val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))//解密val cipherSink = file.sink().cipherSink(cipher)cipherSink.buffer().use { it.write(bytes) }
}

Source输入流进行解密

fun decryptAesToByteString(file: File, key: ByteArray, iv: ByteArray): ByteString {val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))//解密val cipherSource = file.source().cipherSource(cipher)return cipherSource.buffer().use { it.readByteString()}
}

.

.

参考资料

  • okio官网

  • okio 的使用及源码分析


http://www.ppmy.cn/news/882688.html

相关文章

Ichimoku Kinko Hyo

Ichimoku kinko hyo&#xff08;云图指标&#xff09; 云图、或一目均衡表指标&#xff08;Ichimoku kinko hyo&#xff09;&#xff0c;简称IKH&#xff0c;此指标是由笔名为Ichimoku Sanjin的日本记者在1930年代发明的&#xff0c;它是显示市场趋势&#xff0c;趋势的强弱&am…

orika入门使用及组件化

本篇文章&#xff0c;介绍orika的简单使用。以及借助SPI或Spring对orika进行组件化&#xff0c;让增加类型转换规则更加简单方便。 一、orika简介 在工作中&#xff0c;我们经常涉及到对象的DTO、DO等对象的转换。对于这些对象的转换&#xff0c;我们除了自己写大量的get/set方…

IKAnalyzer

IKAnalyzer 下载官网 https://code.google.com/archive/p/ik-analyzer/downloads 点击打开链接 IKAnalyzer2012_u6.zip IK Analyer 2012 完整分发包 upgrade 6 搬运地址 链接: IKAnalyzer2012_u6.zip 密码: srm7 IKAnalyzer 开源项目 http://code.google.com/p…

Orika简单使用

使用orika进行对象间Mapping 1. 当两个类的属性名都一样 package com.orika;/*** Title:CopiedStudent.java* Description:** author zhuyang* version 1.0 2017/7/18*/ public class CopiedStudent {private String name;private Integer age;public String getName() {retur…

Nacos的Java SDK

添加sdk依赖 <dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId><version>${version}</version> </dependency>本文的版本为&#xff1a;2.2.0 配置管理 在界面上添加配置 我们在界面…

A.Mio visits ACGN Exhibition

传送门 题意:该题目的目的是让你求方案数&#xff0c;求方案数的问题其实我们就可以很容易想到动态规划了&#xff0c;因为求方案数一般来说是满足动态规划的两个条件&#xff08;最优子结构&#xff0c;无后效性&#xff09;。 那么我们如何考虑状态呢&#xff0c;我们的状态…

Akka入门

一、什么是Akka&#xff1f; Akka是一个异步非阻塞高并发处理框架&#xff0c;它是一系列框架&#xff0c;包括akka-actor, akka-remote, akka-cluster, akka-stream等&#xff0c;分别具有高并发处理模型——actor模型&#xff0c;远程通信&#xff0c;集群管理&#xff0c;流…

【akka】初识Akka 简单介绍

1.概述 转载&#xff1a;初识Akka 简单介绍 1.1 Flink为什么要用Akka来代替RPC&#xff1f; 原先的RPC服务存在的问题&#xff1a; 没有带回调的异步调用功能&#xff0c;这也是为什么Flink的多个运行时组件需要poll状态的原因&#xff0c;这导致了不必要的延时。没有excep…