RxHttp 完美适配Android 10/11 上传/下载/进度监听
1、前言
随着Android 11的正式发布,适配Android 10/11 分区存储就更加的迫切了,因为Android 11开始,将强制开启分区存储,我们就无法再以绝对路径的方式去读写非沙盒目录下的文件,为此,RxHttp在2.4.0版本中就正式适配了分区存储,并且,可以非常优雅的实现文件上传/下载/进度监听,三步即可搞懂任意请求。
老规矩,先看看请求三部曲
如果你想了解RxHttp更过功能,请查看以下系列文章
gradle依赖
必须
//使用kapt依赖rxhttp-compiler时必须
apply plugin: 'kotlin-kapt'
android {
//必须,java 8或更高
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'com.ljx.rxhttp:rxhttp:2.5.2'
implementation 'com.squareup.okhttp3:okhttp:4.9.0' //rxhttp v2.2.2版本起,需要手动依赖okhttp
kapt 'com.ljx.rxhttp:rxhttp-compiler:2.5.2' //生成RxHttp类,纯Java项目,请使用annotationProcessor代替kapt
}
可选
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [
//使用asXxx方法时必须,告知RxHttp你依赖的rxjava版本,可传入rxjava2、rxjava3
rxhttp_rxjava: 'rxjava3',
rxhttp_package: 'rxhttp' //非必须,指定RxHttp类包名
]
}
}
}
}
dependencies {
implementation 'com.ljx.rxlife:rxlife-coroutine:2.0.1' //管理协程生命周期,页面销毁,关闭请求
//rxjava2 (RxJava2/Rxjava3二选一,使用asXxx方法时必须)
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'com.ljx.rxlife2:rxlife-rxjava:2.0.0' //管理RxJava2生命周期,页面销毁,关闭请求
//rxjava3
implementation 'io.reactivex.rxjava3:rxjava:3.0.6'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'com.ljx.rxlife3:rxlife-rxjava:3.0.0' //管理RxJava3生命周期,页面销毁,关闭请求
//非必须,根据自己需求选择 RxHttp默认内置了GsonConverter
implementation 'com.ljx.rxhttp:converter-fastjson:2.5.2'
implementation 'com.ljx.rxhttp:converter-jackson:2.5.2'
implementation 'com.ljx.rxhttp:converter-moshi:2.5.2'
implementation 'com.ljx.rxhttp:converter-protobuf:2.5.2'
implementation 'com.ljx.rxhttp:converter-simplexml:2.5.2'
}
2、Android 10/11 分区存储
当我们App的targetSdkVersion更改为28以上,并且运行在Android 10以上设备时,我们无法再以绝对路径的方式,去读写非沙盒目录下的文件,当然,如果App是覆盖安装(如:targetSdkVersion 28 覆盖安装为 29),则会保持原来的访问方式。
requestLegacyExternalStorage属性
如果我们的app将targetSdkVersion更改为28以上,且想保持原来的访问方式,则需要在清单文件中将 requestLegacyExternalStorage 的值设置为 true,如下:
<manifest ...>
<!-- This attribute is "false" by default on apps targeting
Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
此时,便可继续以原来的方式去读写文件,然而,在Android 11上,Google又给了它新的含义,来看看官网的原话
也就是说,在Android 11设备上,targetSdkVersion为29以上的app,将强制开启分区存储,requestLegacyExternalStorage属性失效
注意,只要同时满足以上两个条件,不管是覆盖安装还是requestLegacyExternalStorage = true,都会强制开启分区存储
分区存储优势
对用户来说,解决了文件乱放的现象
对于开发者来说,我们无需写权限,就可以在分区目录下创建文件,并且访问自己创建的文件,不需要读权限(访问其它应用创建的文件,还是需要读权限)
新的文件访问方式
此图来源于作者[连续三届村草]分享的Android 10(Q)/11(R) 分区存储适配一文,感谢作者的总结
3、上传
3.1、简单上传
在介绍Android 10文件上传前,我们先来看看Android 10之前是如何上传文件的,如下:
//kotlin 协程
val result = RxHttp.postForm("/service/...")
.add("key", "value")
.addFile("file", File("xxx/1.jpg"))
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm("/service/...")
.add("key", "value")
.addFile("file", File("xxx/1.jpg"))
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
以上,我们仅需调用 addFile方法添加文件对象即可,RxHttp提供了一系列addFile方法,列出几个常用的,如下:
//添加单个文件
addFile(String, File)
//添加多个文件,每个文件对应相同的key
addFile(String, List<? extends File> fileList)
//添加多个文件,每个文件对应不同的key
addFile(Map<String, ? extends File> fileMap)
//等等其它addFile方法
在Android 10,我们需要通过Uri对象去上传文件,在RxHttp中,通过addPart方法添加Uri对象,如下:
val context = getContext(); //获取上下文对象
//获取Uri对象,这里为了方便,随便写了一个Downlaod目录下的Uri地址
val uri = Uri.parse("content://media/external/downloads/13417")
//kotlin 协程
val result = RxHttp.postForm("/service/...")
.add("key", "value")
.addPart(context, "file", uri)
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm("/service/...")
.add("key", "value")
.addPart(context, "file", uri)
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
同样的,RxHttp内部提供了一系列addPart方法供大家选择,列出几个常用的,如下:
//添加单个Uri对象
addPart(Context, String, Uri)
//添加多个Uri对象,每个Uri对应相同的key
addParts(Context,String, List<? extends Uri> uris)
//添加多个Uri对象,每个Uri对应不同的key
addParts(Context context, Map<String, ? extends Uri> uriMap)
//等等其它addPart方法
3.2、带进度上传
老规矩,看看Android 10之前是如何监听上传进度的,如下:
//kotlin 协程
val result = RxHttp.postForm("/service/...")
.add("key", "value")
.addFile("file", File("xxx/1.jpg"))
.upload(this) {//this为当前协程CoroutineScope对象,用于控制回调线程
//上传进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm("/service/...")
.add("key", "value")
.addFile("file", File("xxx/1.jpg"))
.upload(AndroidSchedulers.mainThread()) {
//上传进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
相比于单纯的上传文件,我们仅需额外调用upload操作符,传入线程调度器及进度回调即可。
同样的,对于Andorid 10,我们仅需要将File对象换成Uri对象即可,如下:
val context = getContext(); //获取上下文对象
//获取Uri对象,这里为了方便,随便写了一个Downlaod目录下的Uri地址
val uri = Uri.parse("content://media/external/downloads/13417")
//kotlin 协程
val result = RxHttp.postForm("/service/...")
.add("key", "value")
.addPart(context, "file", uri)
.upload(this) {//this为当前协程CoroutineScope对象,用于控制回调线程
//上传进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm("/service/...")
.add("key", "value")
.addPart(context, "file", uri)
.upload(AndroidSchedulers.mainThread()) {
//上传进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
怎么样?是不是so easy!!
4、下载
下载较于上传,要丰富很多,RxHttp内部提供类一系列下载方法来满足不同的需求,如下:
//kotlin
fun IRxHttp.toDownload(
destPath: String,
context: CoroutineContext? = null,
progress: (suspend (ProgressT<String>) -> Unit)? = null
)
fun IRxHttp.toDownload(
context: Context,
uri: Uri,
coroutineContext: CoroutineContext? = null,
progress: (suspend (ProgressT<Uri>) -> Unit)? = null
)
fun <T> IRxHttp.toDownload(
osFactory: OutputStreamFactory<T>,
context: CoroutineContext? = null,
progress: (suspend (ProgressT<T>) -> Unit)? = null
)
4.1、简单下载
在Android 10之前,我们仅需传入一个本地文件路径即可,如下:
val localPath = "/sdcard/.../xxx.apk"
//kotlin 协程
val result = RxHttp.get("/service/.../xxx.apk")
.toDownload(localPath)
.await() //这里返回sd卡存储路径
//RxJava
RxHttp.get("/service/.../xxx.apk")
.asDownload(localPath)
.subscribe({
//成功回调,这里返回sd卡存储路径
}, {
//异常回调
})
而到了Android 10,我们需要自定义一个Android10DownloadFactory类,继承UriFactory类,如下:
class Android10DownloadFactory @JvmOverloads constructor(
context: Context,
fileName: String,
queryUri: Uri? = null
) : UriFactory(context, queryUri, fileName) {
override fun getUri(response: Response): Uri {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues().run {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) //文件名
//取contentType响应头作为文件类型
put(MediaStore.MediaColumns.MIME_TYPE, response.body?.contentType().toString())
//下载到Download目录
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
val uri = queryUri ?: MediaStore.Downloads.EXTERNAL_CONTENT_URI
context.contentResolver.insert(uri, this)
} ?: throw NullPointerException("Uri insert fail, Please change the file name")
} else {
Uri.fromFile(File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), displayName))
}
}
}
这里简单介绍下上面的代码,本文后续会详细介绍为啥定义这样一个类,以及如何构建一个Uri对象。
- 首先就是继承
UriFactory抽象类,实现getUri(Response)方法 - 接着就在实现方法里判断SDK版本,Android 10以上,就通过
contentResolver构建Uri对象,否则就根据File对象构建Uri对象,这样就可以兼容到所有系统版本 - 最后返回
Uri对象即可
注:以上代码,基本可以满足大部分人的需求,如你有特殊需求,构建Uri的过程的作出简单的修改即可
有了Android10DownloadFactory类,执行Android 10下载就会及其方便,如下:
val factory = Android10DownloadFactory(context, "test.apk")
//kotlin 协程
val uri = RxHttp.get("/service/.../xxx.apk")
.toDownload(factory)
.await() //这里返回工厂类构建的Uri对象
//RxJava
RxHttp.get("/service/.../xxx.apk")
.asDownload(factory)
.subscribe({
//成功回调,这里返回工厂类构建的Uri对象
}, {
//异常失败
})
以上asDownload、toDownload方法都接收一个UriFactory类型参数,故我们可以直接传入Android10DownloadFactory对象。
4.2、带进度下载
对于带进度下载,我们只需要调用asDownload、toDownload方法时,传入线程调度器及进度回调即可,如下:
Android 10之前
val localPath = "/sdcard/.../xxx.apk"
//kotlin 协程
val result = RxHttp.get("/service/.../xxx.apk")
.toDownload(localPath, Dispatchers.Main) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.await() //这里返回sd卡存储路径
//RxJava
RxHttp.get("/service/.../xxx.apk")
.asDownload(localPath, AndroidSchedulers.mainThread()) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.subscribe({
//成功回调,这里返回sd卡存储路径
}, {
//异常失败
})
Android 10以上,传入我们定义的Android10DownloadFactory对象的同时,再传入传入线程调度器及进度监听即可,如下:
val factory = Android10DownloadFactory(context, "test.apk")
//kotlin 协程
val uri = RxHttp.get("/service/.../xxx.apk")
.toDownload(factory, Dispatchers.Main) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.await() //这里返回工厂类构建的Uri对象
//RxJava
RxHttp.get("/service/.../xxx.apk")
.asDownload(factory, AndroidSchedulers.mainThread()) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.subscribe({
//成功回调,这里返回工厂类构建的Uri对象
}, {
//异常失败
})
4.3、带进度断点下载
对于断点下载,我们需要调用一系列asAppendDownload、toAppendDownload方法,可以把它们理解为追加下载,实现如下:
Android 10之前
val localPath = "/sdcard/.../xxx.apk"
//kotlin 协程
val result = RxHttp.get("/service/.../xxx.apk")
.toAppendDownload(localPath, Dispatchers.Main) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.await() //这里返回sd卡存储路径
//RxJava
RxHttp.get("/service/.../xxx.apk")
.asAppendDownload(localPath, AndroidSchedulers.mainThread()) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.subscribe({
//成功回调,这里返回sd卡存储路径
}, {
//异常失败
})
在Android 10上,有一点需要注意的是,我们在构建Android10DownloadFactory对象时,需要传入第三个参数queryUri,可以把它理解为要查询的文件夹,断点下载,RxHttp内部会根据文件名在指定的文件夹下查找对应的文件,得到当前文件的长度,也就是断点位置,从而告诉服务端从哪里开始下载,如下:
val queryUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
//在Download目录下查找test.apk文件
val factory = Android10DownloadFactory(context, "test.apk", queryUri)
//kotlin 协程
val uri = RxHttp.get("/service/.../xxx.apk")
.toAppendDownload(factory, Dispatchers.Main) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.await() //这里返回工厂类构建的Uri对象
//RxJava
RxHttp.get("/service/.../xxx.apk")
.asAppendDownload(factory, AndroidSchedulers.mainThread()) {
//下载进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.progress //当前进度 0-100
val currentSize = it.currentSize //当前已上传的字节大小
val totalSize = it.totalSize //要上传的总字节大小
}
.subscribe({
//成功回调,这里返回工厂类构建的Uri对象
}, {
//异常失败
})
5、如何构建Uri对象?
在上面代码中,我们自定义了Android10DownloadFactory类,其中最为关键的代码就是如何构建一个Uri对象,接下来,就教大家如何去构建一个Uri,马上开始,如下:
public Uri getUri(Context context) {
ContentValues values = new ContentValues();
//1、配置文件名
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "1.jpg");
//2、配置文件类型
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
//3、配置存储目录
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
//4、将配置好的对象插入到某张表中,最终得到Uri对象
return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
第一步,配置文件名称,这个就没啥好说的了
第二步,配置文件类型,每个文件都应该有一个类型描述,这样,后续查找时,就可以根据这个类型去查找出同一类型的文件,如:查找相册,此属性是可选的,如果不配置,后续就无法根据类型查找到这个文件
第三步,配置存储目录,这个是相对路径,总共有10个目录可选,如下:
Environment.DIRECTORY_DOCUMENTS对应路径:/storage/emulated/0/Documents/Environment.DIRECTORY_DOWNLOADS对应路径:/storage/emulated/0/Download/Environment.DIRECTORY_DCIM对应路径:/storage/emulated/0/DCIM/Environment.DIRECTORY_PICTURES对应路径:/storage/emulated/0/Pictures/Environment.DIRECTORY_MOVIES对应路径:/storage/emulated/0/Movies/Environment.DIRECTORY_ALARMS对应路径:/storage/emulated/0/Alrams/Environment.DIRECTORY_MUSIC对应路径:/storage/emulated/0/Music/Environment.DIRECTORY_NOTIFICATIONS对应路径:/storage/emulated/0/Notifications/Environment.DIRECTORY_PODCASTS对应路径:/storage/emulated/0/Podcasts/Environment.DIRECTORY_RINGTONES对应路径:/storage/emulated/0/Ringtones/
如果需要在以上目录下,创建子目录,则传入的时候,直接带上即可,如下
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/RxHttp");
第四步,插入到对应的表中,总共有5张表可选,如下:
- 存储图片:
MediaStore.Images.Media.EXTERNAL_CONTENT_URI - 存储视频:
MediaStore.Video.Media.EXTERNAL_CONTENT_URI - 存储音频:
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI - 存储任意文件:
MediaStore.Downloads.EXTERNAL_CONTENT_URI - 存储任意文件:
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
- 存储图片:
需要特殊说明下,以上5张表中,只能存入对应文件类型的信息,如我们不能将音频文件信息,插入到MediaStore.Images.Media.EXTERNAL_CONTENT_URI图片表中,插入时,系统会直接抛出异常
注意事项
以上5张表中,除了对插入的文件类型有限制外,还对要插入的相对路径有限制,如,我们将一个apk文件下载/storage/emulated/0/Download/RxHttp/目录下,并插入到图片表中,如下:
public Uri getUri(Context context) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "1.apk");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/RxHttp");
return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
当执行到insert操作时,系统将会直接报错,报错信息如下:
Primary directory Download not allowed for content://media/external/images/media; allowed directories are [DCIM, Pictures]
大致意思就是,Download目录不允许插入到MediaStore.Images.Media.EXTERNAL_CONTENT_URI表中,该表只允许插入DCIM和Pictures目录
6、小结
开源不易,写文章更不易,喜欢的话,还需劳烦大家给本文点个赞,可以的话,再给个star,我将感激不尽,
RxHttp 完美适配Android 10/11 上传/下载/进度监听的更多相关文章
- 使用Typescript重构axios(二十五)——文件上传下载进度监控
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- SSM + Android 网络文件上传下载
SSM + Android 网络交互的那些事 2016年12月14日 17:58:36 ssm做为后台与android交互,相信只要是了解过的人都知道一些基本的数据交互,向json,对象,map的交互 ...
- FTP 上传下载 进度条
11 /// <summary> /// 文件上传 /// </summary> /// <param name="filePath">原路径( ...
- 详细解读XMLHttpRequest(二)响应属性、二进制数据、监测上传下载进度
本文主要参考:MDN 分析并操作 responseXML属性 如果你使用 XMLHttpRequest 来获得一个远程的 XML 文档的内容,responseXML 属性将会是一个由 XML 文档解析 ...
- ajax 上传文件,监听进度(progress)
mdn 前端代码 github <body class="m-2"> <label for="a" class="btn btn-p ...
- jq 上传下载进度条
里面只演示了下载的,挂载的是我的七牛服务器上的内容,上传事件和下载是一模一样的,为了大家不乱上传东西到我的服务器,而且我的服务器容量也不大,这里只展示了下载.代码: <!DOCTYPE html ...
- html5上传文件并监听进度
出处: http://blog.csdn.net/small_rice_/article/details/21391625
- 一行代码实现Okhttp,Retrofit,Glide下载上传进度监听
https://mp.weixin.qq.com/s/bopDUFMB7EiK-MhLc3KDXQ essyan 鸿洋 2017-06-29 本文作者 本文由jessyan投稿. jessyan的博客 ...
- OkHttp 优雅封装 HttpUtils 之 上传下载解密
曾经在代码里放荡不羁,如今在博文中日夜兼行,只为今天与你分享成果.如果觉得本文有用,记得关注我,我将带给你更多. 还没看过第一篇文章的欢迎移步:OkHttp 优雅封装 HttpUtils 之气海雪山初 ...
随机推荐
- 「考试」CSP-S 2020
乱扯 爆炸的过程是这样的 写了\(2.5h\)的\(T1\)过不去大样例,自闭了 决定调\(T2\)然后过了样例但事实上写的完全是假的 这个时候突然\(T1\)灵光一闪就没再看\(T2\)了 然后就一 ...
- 笔记本无法连接校园网,windows诊断显示校园网之未响应
打开cmd(管理员): 输入以下四条,每一条都按enter ipconfig /flushdns ipconfig /registerdns ipconfig /release ipconfig / ...
- Java数据结构(十三)—— 二叉排序树(BST)
二叉排序树(BST) 需求 给定数列{7,3,10,12,5,1,9},要求能够高效的完成对数据的查询和添加 思路三则 使用数组,缺点:插入和排序速度较慢 链式存储,添加较快,但查找速度慢 使用二叉排 ...
- Python+爬虫+xlwings发现CSDN个人博客热门文章
☞ ░ 前往老猿Python博文目录 ░ 一.引言 最近几天老猿博客的访问量出现了比较大的增长,从常规的1000-3000之间波动的范围一下子翻了将近一倍,粉丝增长从日均10-40人也增长了差不多一倍 ...
- 第7.6节 Python中类的继承机制详述
在本章第一节,介绍了面向对象程序设计的三个特征:封装.继承和多态,前面章节重点介绍了封装和多态,由于Python语言是多态语言,对象的类型不再由继承等方式决定,而由实际运行时所表现出的具体行为来决定, ...
- 第11.14节 正则表达式转义符和Python转义符相同引发问题的解决办法
正则表达式使用反斜杠('\')来把特殊字符转义成普通字符(为了方便称为"正则表达式转义"),而反斜杠在普通的 Python 字符串里也是转义符(称为"字符串转义" ...
- [SWPU2019]Web1 空格过滤用/**/ 注释过滤闭合单引号 imformation_schema.columns/tables过滤 用5.7新特性 或无名注入(此处database()不能用)
0x00 知识点 二次注入流程分析 二次注入漏洞在CTF中常见于留言板和注册登录功能,简单来说可以分为两个步骤: 插入恶意数据(发布帖子,注册账号),用mysql_escape_string()函数对 ...
- mysql 创建新用户、数据库、授权
创建用户 1.登录mysql mysql -uroot -p 2.创建本地用户(2.3选其一) #use mysql; //选择mysql数据库 #create user 'w ...
- pytorch实战(一)hw1——李宏毅老师作业1
任务描述:利用前9小时数据,预测第10小时的pm2.5的数值,回归任务 kaggle地址:https://www.kaggle.com/c/ml2020spring-hw1 训练集为: 12个月*20 ...
- 【Dotnet9-01】从0开始搭建开源项目-lqclass.com
行文目录 一. 前言 1.1 我的现有网站 1.2 想法:新开发一个网站 1.3 目前开发计划 二. 行动了 2.1 Github创建项目 2.2 使用 WTM 搭建后台框架 2.3 项目演示 2.4 ...