【鸿蒙应用ArkTS开发系列】- 选择图片、文件和拍照功能实现

文章目录

  • 前言
  • 创建多媒体Demo工程
  • 创建MediaBean 实体类
  • 创建MediaHelper工具类
  • API标记弃用问题
  • 动态申请多媒体访问权限
  • 实现选择图片显示功能
  • 打包测试

前言

在使用App的时候,我们经常会在一些社交软件中聊天时发一些图片或者文件之类的多媒体文件,那在鸿蒙原生应用中,我们怎么开发这样的功能呢? 本文会给大家对这个功能点进行讲解,我们采用的是拉起系统组件来进行图片、文件的选择,拉起系统相机进行拍照的这样一种实现方式。
在文章开始之前,按照惯例,我们先展示本文Demo效果图:

在这里插入图片描述

下面我们正式开始讲解。

创建多媒体Demo工程

我们使用Empty 模板创建一个Demo工程。
在这里插入图片描述
在这里插入图片描述

创建MediaBean 实体类

在src->main->ets 下面创建bean文件夹,在文件夹下创建MediaBean.ts文件

/*** 多媒体数据类*/
export class MediaBean {/*** 文件名称*/public fileName: string;/*** 文件大小*/public fileSize: number;/*** 文件类型*/public fileType: string;/*** 本地存储地址*/public localUrl: string;
}

创建MediaHelper工具类

在src->main->ets 下面创建helper文件夹,在文件夹下创建MediaHelper.ts文件

在这里插入图片描述
在这里插入图片描述

/*** 多媒体辅助类*/
export class MediaHelper {private readonly TAG: string = 'MediaHelper';private mContext: common.Context;constructor(context: common.Context) {this.mContext = context;}/*** 选择图片*/public selectPicture(): Promise<MediaBean> {}/*** 选择文件*/public selectFile(): Promise<MediaBean> {}/*** 拍照*/public async takePhoto(context: common.UIAbilityContext): Promise<MediaBean> {}/*** 封装附件实体类*  * @param uri 文件路径*/private async buildMediaBean(uri: string): Promise<MediaBean> {}/*** 通过Uri查找所选文件信息,插入到MediaBean中* @param mediaBean* @param uri*/private async appendFileInfoToMediaBean(mediaBean: MediaBean, uri: string) {}
}

MediaHelper 类定义了5个方法,

  • selectPicture 提供选择图片功能
  • selectFile 提供选择文件功能
  • takePhoto 提供拍照功能
  • buildMediaBean 内部方法,提供MediaBean对象封装
  • appendFileInfoToMediaBean 内部方法,提供追加查询所选文件的文件信息的功能

通过系统组件选择图片、文件或者拍照之后,系统只是简单的返回一个文件的Uri,如果我们需要展示文件的名称、文件大小、文件类型,需要通过appendFileInfoToMediaBean 方法另外去获取。

下面我们针对这几个方法,增加具体的实现代码:

  1. selectPicture
/*** 选择图片*/public selectPicture(): Promise<MediaBean> {try {let photoSelectOptions = new picker.PhotoSelectOptions();photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;photoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();return photoPicker.select(photoSelectOptions).then((photoSelectResult) => {Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));if (photoSelectResult && photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) {let filePath = photoSelectResult.photoUris[0];Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + filePath);return filePath;}}).catch((err) => {Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);return err;}).then(async (filePath) => {const mediaBean = await this.buildMediaBean(filePath);return mediaBean;});} catch (err) {Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);return Promise.reject(err);}}

选择图片的功能,我们通过系统组件 picker.PhotoViewPicker 来进行图片选择,通过配置PhotoSelectOptions,指定选择的MIMEType类型(这里PhotoViewMIMETypes.IMAGE_TYPE 图片类型) 、选择的图片最大数量 maxSelectNumber ,这里我们实现单选功能,数值设置为1即可。
使用photoPicker.select 拉起系统组件进行选择,然后在回调中获取图片的uri。

  1. selectFile
/*** 选择文件*/public selectFile(): Promise<MediaBean> {try {let documentSelectOptions = new picker.DocumentSelectOptions();let documentPicker = new picker.DocumentViewPicker();return documentPicker.select(documentSelectOptions).then((documentSelectResult) => {Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + JSON.stringify(documentSelectResult));if (documentSelectResult && documentSelectResult.length > 0) {let filePath = documentSelectResult[0];Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + filePath);return filePath;}}).catch((err) => {Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);return err;}).then(async (filePath) => {const mediaBean = await this.buildMediaBean(filePath);return mediaBean;});} catch (err) {Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);return Promise.reject(err);}}

选择文件的功能,我们通过系统组件 picker.DocumentViewPicker来进行文件选择,代码基本是跟图片选择是一样的,区别在于DocumentSelectOptions,目前api9并没有配置项提供,具体关注后续的api版本情况。

  1. takePhoto
/*** 拍照*/public async takePhoto(context: common.UIAbilityContext): Promise<MediaBean> {let want = {'uri': '','action': wantConstant.Action.ACTION_IMAGE_CAPTURE,'parameters': {},};return context.startAbilityForResult(want).then((result) => {Log.info(this.TAG, `startAbility call back , ${JSON.stringify(result)}`);if (result.resultCode === 0 && result.want && StringUtils.isNotNullOrEmpty(result.want.uri)) {//拍照成功Log.info(this.TAG, 'takePhoto successfully, takePhotoResult uri: ' + result.want.uri);return result.want.uri;}}).catch((error) => {Log.info(this.TAG, `startAbility error , ${JSON.stringify(error)}`);return error;}).then(async (uri: string) => {const mediaBean = await this.buildMediaBean(uri);return mediaBean;});}

拍照的功能,我们也是拉起相机来进行拍照的,我们使用 startAbilityForResult 方法 + 配置拉起action (wantConstant.Action.ACTION_IMAGE_CAPTURE)的方式拉起系统相机,拍照结束后,在then中接收返回的数据,我们通过返回码result.resultCode 来判断是否进行了拍照,如果状态值===0,说明进行了拍照,我们再使用result.want.uri获取拍照后的照片uri。

  1. buildMediaBean
  /*** 封装多媒体实体类** @param uri 文件路径*/private async buildMediaBean(uri: string): Promise<MediaBean> {if (StringUtils.isNullOrEmpty(uri)) {return null;}const mediaBean: MediaBean = new MediaBean();mediaBean.localUrl = uri;await this.appendFileInfoToMediaBean(mediaBean, uri);return mediaBean;}

这个方法的作用主要是封装一个多媒体实体类,并触发appendFileInfoToMediaBean 获取Uri对应文件的一些文件信息。代码很简单,相信大家一目了然。

  1. appendFileInfoToMediaBean
/*** 通过Uri查找所选文件信息,插入到MediaBean中* @param mediaBean* @param uri*/private async appendFileInfoToMediaBean(mediaBean: MediaBean, uri: string) {if (StringUtils.isNullOrEmpty(uri)) {return;}let fileList: Array<mediaLibrary.FileAsset> = [];const parts: string[] = uri.split('/');const id: string = parts.length > 0 ? parts[parts.length - 1] : '-1';try {let media = mediaLibrary.getMediaLibrary(this.mContext);let mediaFetchOptions: mediaLibrary.MediaFetchOptions = {selections: mediaLibrary.FileKey.ID + '= ?',selectionArgs: [id],uri: uri};let fetchFileResult = await media.getFileAssets(mediaFetchOptions);Log.info(this.TAG, `fileList getFileAssetsFromType fetchFileResult.count = ${fetchFileResult.getCount()}`);fileList = await fetchFileResult.getAllObject();fetchFileResult.close();await media.release();} catch (e) {Log.error(this.TAG, "query: file data  exception ");}if (fileList && fileList.length > 0) {let fileInfoObj = fileList[0];Log.info(this.TAG, `file id = ${JSON.stringify(fileInfoObj.id)} , uri = ${JSON.stringify(fileInfoObj.uri)}`);Log.info(this.TAG, `file fileList displayName = ${fileInfoObj.displayName} ,size = ${fileInfoObj.size} ,mimeType = ${fileInfoObj.mimeType}`);mediaBean.fileName = fileInfoObj.displayName;mediaBean.fileSize = fileInfoObj.size;mediaBean.fileType = fileInfoObj.mimeType;}}

这个方法的作用主要是通过uri查询文件的详细信息,包括文件名称、文件大小、文件类型。

  • 通过Uri获取文件ID。
  • 使用mediaLibrary.getMediaLibrary获取media对象。
  • 配置MediaFetchOptions,主要是ID,通过文件ID来查找文件对象。
  • 使用media.getFileAssets查询文件对象结果,这里可以是批量操作,得到一个FetchFileResult对象。
  • 遍历FileAsset数组,得到文件信息。

这里列下FileAsset的一些字段:

名称类型可读可写说明
idnumber文件资源编号
uristring文件资源uri(如:datashare:///media/image/2)
mimeTypestring文件扩展属性
mediaType8+MediaType媒体类型
displayNamestring显示文件名,包含后缀名
titlestring文件标题
relativePath8+string相对公共目录路径
parent8+number父目录id
sizenumber文件大小(单位:字节)
dateAddednumber添加日期(添加文件时间到1970年1月1日的秒数值)
dateModifiednumber修改日期(修改文件时间到1970年1月1日的秒数值,修改文件名不会改变此值,当文件内容发生修改时才会更新)
dateTakennumber拍摄日期(文件拍照时间到1970年1月1日的秒数值)
artist8+string作者
audioAlbum8+string专辑
widthnumber图片宽度(单位:像素)
heightnumber图片高度(单位:像素)
orientationnumber图片显示方向(顺时针旋转角度,如0,90,180 单位:度)
duration8+number持续时间(单位:毫秒)
albumIdnumber文件所归属的相册编号
albumUri8+string文件所归属相册uri
albumNamestring文件所归属相册名称

这里贴下MediaHelper.ts的完整代码

import common from '@ohos.app.ability.common';
import picker from '@ohos.file.picker';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
import wantConstant from '@ohos.ability.wantConstant';
import { MediaBean } from '../bean/MediaBean';
import { StringUtils } from '../utils/StringUtils';
import { Log } from '../utils/Log';/*** 多媒体辅助类*/
export class MediaHelper {private readonly TAG: string = 'MediaHelper';private mContext: common.Context;constructor(context: common.Context) {this.mContext = context;}/*** 选择图片*/public selectPicture(): Promise<MediaBean> {try {let photoSelectOptions = new picker.PhotoSelectOptions();photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;photoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();return photoPicker.select(photoSelectOptions).then((photoSelectResult) => {Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));if (photoSelectResult && photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) {let filePath = photoSelectResult.photoUris[0];Log.info(this.TAG, 'PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + filePath);return filePath;}}).catch((err) => {Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);return err;}).then(async (filePath) => {const mediaBean = await this.buildMediaBean(filePath);return mediaBean;});} catch (err) {Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);return Promise.reject(err);}}/*** 选择文件*/public selectFile(): Promise<MediaBean> {try {let documentSelectOptions = new picker.DocumentSelectOptions();let documentPicker = new picker.DocumentViewPicker();return documentPicker.select(documentSelectOptions).then((documentSelectResult) => {Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + JSON.stringify(documentSelectResult));if (documentSelectResult && documentSelectResult.length > 0) {let filePath = documentSelectResult[0];Log.info(this.TAG, 'DocumentViewPicker.select successfully, DocumentSelectResult uri: ' + filePath);return filePath;}}).catch((err) => {Log.error(this.TAG, 'PhotoViewPicker.select failed with err: ' + err);return err;}).then(async (filePath) => {const mediaBean = await this.buildMediaBean(filePath);return mediaBean;});} catch (err) {Log.error(this.TAG, 'PhotoViewPicker failed with err: ' + err);return Promise.reject(err);}}/*** 拍照*/public async takePhoto(context: common.UIAbilityContext): Promise<MediaBean> {let want = {'uri': '','action': wantConstant.Action.ACTION_IMAGE_CAPTURE,'parameters': {},};return context.startAbilityForResult(want).then((result) => {Log.info(this.TAG, `startAbility call back , ${JSON.stringify(result)}`);if (result.resultCode === 0 && result.want && StringUtils.isNotNullOrEmpty(result.want.uri)) {//拍照成功Log.info(this.TAG, 'takePhoto successfully, takePhotoResult uri: ' + result.want.uri);return result.want.uri;}}).catch((error) => {Log.info(this.TAG, `startAbility error , ${JSON.stringify(error)}`);return error;}).then(async (uri: string) => {const mediaBean = await this.buildMediaBean(uri);return mediaBean;});}/*** 封装多媒体实体类** @param uri 文件路径*/private async buildMediaBean(uri: string): Promise<MediaBean> {if (StringUtils.isNullOrEmpty(uri)) {return null;}const mediaBean: MediaBean = new MediaBean();mediaBean.localUrl = uri;await this.appendFileInfoToMediaBean(mediaBean, uri);return mediaBean;}/*** 通过Uri查找所选文件信息,插入到MediaBean中* @param mediaBean* @param uri*/private async appendFileInfoToMediaBean(mediaBean: MediaBean, uri: string) {if (StringUtils.isNullOrEmpty(uri)) {return;}let fileList: Array<mediaLibrary.FileAsset> = [];const parts: string[] = uri.split('/');const id: string = parts.length > 0 ? parts[parts.length - 1] : '-1';try {let media = mediaLibrary.getMediaLibrary(this.mContext);let mediaFetchOptions: mediaLibrary.MediaFetchOptions = {selections: mediaLibrary.FileKey.ID + '= ?',selectionArgs: [id],uri: uri};let fetchFileResult = await media.getFileAssets(mediaFetchOptions);Log.info(this.TAG, `fileList getFileAssetsFromType fetchFileResult.count = ${fetchFileResult.getCount()}`);fileList = await fetchFileResult.getAllObject();fetchFileResult.close();await media.release();} catch (e) {Log.error(this.TAG, "query: file data  exception ");}if (fileList && fileList.length > 0) {let fileInfoObj = fileList[0];Log.info(this.TAG, `file id = ${JSON.stringify(fileInfoObj.id)} , uri = ${JSON.stringify(fileInfoObj.uri)}`);Log.info(this.TAG, `file fileList displayName = ${fileInfoObj.displayName} ,size = ${fileInfoObj.size} ,mimeType = ${fileInfoObj.mimeType}`);mediaBean.fileName = fileInfoObj.displayName;mediaBean.fileSize = fileInfoObj.size;mediaBean.fileType = fileInfoObj.mimeType;}}
}

API标记弃用问题

上面的代码,在api9实测是可以正常使用的,但是有一些API被标记为过期,有一些在官方文档注明即将停用,但是我没有找到可以平替的API,如果有读者知道的,麻烦评论区告诉我一声,谢谢。

  1. ohos.app.ability.wantConstant
    官方提示让我们切换到 ohos.app.ability.wantConstant这个类下,可是我们用到wantConstant.Action,这个Action在 ohos.app.ability.wantConstant中没有定义,我在SDK中也没有找到Action在哪一个类中定义;
  2. mediaLibrary.getMediaLibrary.getFileAssets
    我们需要使用getMediaLibrary获取多媒体对象,调用getFileAssets查询文件的多媒体信息,官方提示让我们使用ohos.file.picker,可笑的是picker中没有getFileAssets 相关的方法,那我们通过picker只能拿到一个文件的Uri,文件名称、文件大小这些常规的文件相关的数据都拿不到,那功能都无法开发,这也是我之前的一个疑问。

动态申请多媒体访问权限

我们读取文件的多媒体信息需要申请一个多媒体的读取权限 ohos.permission.READ_MEDIA,这个权限需要在
module.json5中添加配置requestPermissions,在该节点下配置READ_MEDIA权限,具体如下图:
在这里插入图片描述
由于这个READ_MEDIA权限需要进行动态权限申请,因为还需要我们进行动态权限申请代码逻辑开发,这里由于篇幅原因,我就不过多赘述,后续如果对这块动态权限申请有不明白的地方,我再重新写一篇文章介绍,讲下动态申请权限,跳转系统权限设置页配置权限这些功能具体如何实现。
这次的Demo,我们直接安装后,在系统设置中找到应用,把对应的权限开启即可(绕过权限动态申请)。

实现选择图片显示功能

下面我们编写UI页面,使用我们上面的MediaHelper工具类选择图片、拍照,并将图片显示出来。
在这里插入图片描述

我们在Index.ets文件中放三个按钮,以及显示文件名称、大小、文件类型以及文件路径、显示图片的控件。
完整的代码如下:

import common from '@ohos.app.ability.common';
import { MediaBean } from '../bean/MediaBean';
import { MediaHelper } from '../helper/MediaHelper';@Entry
@Component
struct Index {@State mediaBean: MediaBean = new MediaBean();private mediaHelper: MediaHelper = new MediaHelper(getContext());build() {Row() {Column() {Text('选择图片').textAlign(TextAlign.Center).width(200).fontSize(16).padding(10).margin(20).border({ width: 0.5, color: '#ff38f84b', radius: 15 }).onClick(() => {this.handleClick(MediaOption.Picture)})Text('选择文件').textAlign(TextAlign.Center).width(200).fontSize(16).padding(10).margin(20).border({ width: 0.5, color: '#ff38f84b', radius: 15 }).onClick(() => {this.handleClick(MediaOption.File)})Text('拍照').textAlign(TextAlign.Center).width(200).fontSize(16).padding(10).margin(20).border({ width: 0.5, color: '#ff38f84b', radius: 15 }).onClick(() => {this.handleClick(MediaOption.TakePhoto)})Divider().width('100%').height(0.5).color('#ff99f6a2').margin({ top: 20 }).padding({ left: 20, right: 20 })Text(`文件名称: ${this.mediaBean.fileName ? this.mediaBean.fileName : ''}`).textAlign(TextAlign.Center).width('100%').fontSize(16).margin(10)Text(`文件大小: ${this.mediaBean.fileSize ? this.mediaBean.fileSize : ''}`).textAlign(TextAlign.Center).width('100%').fontSize(16).margin(10)Text(`文件类型: ${this.mediaBean.fileType ? this.mediaBean.fileType : ''}`).textAlign(TextAlign.Center).width('100%').fontSize(16).margin(10)Text(`文件Uri: ${this.mediaBean.localUrl ? this.mediaBean.localUrl : ''}`).textAlign(TextAlign.Center).width('100%').fontSize(16).margin(10)Image(this.mediaBean.localUrl).width(300).height(300).backgroundColor(Color.Grey)}.width('100%').height('100%')}.height('100%')}async handleClick(option: MediaOption) {let mediaBean: MediaBean;switch (option) {case MediaOption.Picture:mediaBean = await this.mediaHelper.selectPicture();break;case MediaOption.File:mediaBean = await this.mediaHelper.selectFile();break;case MediaOption.TakePhoto:mediaBean = await this.mediaHelper.takePhoto(getContext() as common.UIAbilityContext);break;default:break;}if (mediaBean) {this.mediaBean = mediaBean;}}
}enum MediaOption {Picture = 0,File = 1,TakePhoto = 2
}

打包测试

打包安装到真机上,需要我们给项目配置签名信息。我们点击File -> Project Structure ->Project ,选择 Signing Configs面板,勾选 Support HarmonyOS 跟Automatically generate signature,自动生成调试签名,生成完毕后,运行安装到手机上。

注意:由于我们没有实现多媒体读取权限动态申请权限,因此需要在手机系统设置-应用中找到该应用,开启多媒体权限,该权限默认是禁止的,开启后再打开应用操作即可。运行的具体的效果如文章开头贴图展示一般。

文本到此完毕,有疑问的请在评论区留言交流,谢谢阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/180307.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GitHub 2023排名前十的最佳开源项目

开源软件&#xff08;OSS&#xff09;彻底改变了当今软件开发的方式。在数百万个开源GitHub项目中&#xff0c;要找到最适合需求的开源项目可能会让人不知所措。 今天给大家列出2023年增长最快的前10个开源GitHub仓库。通过这些增长最快的开源项目&#xff0c;也可以从整体上了…

算法 离散化

整数离散化 适用条件 适用于有序的整数序列该序列的值域很大&#xff0c;该序列的数的个数很少使用的是数的相对大小而非绝对大小 算法思路 原数组 a &#xff1a; 数组下标&#xff1a;0 1 2 3 4 数组元素&#xff1a;1 2 2 5 109 映射数组 &#xff1a; 数组下标&…

Flask教程入门

1.学习Flask之前&#xff0c;首先需要对URL进行一定的了解。 URL的一些知识&#xff1a; 1.URL只能包含ASCII码里面一些可显示的字符&#xff0c;如A-Z&#xff0c;a-z&#xff0c;0-9&#xff0c;&&#xff0c;#&#xff0c;%&#xff0c;&#xff1f;&#xff0c;/等字符…

数据链路层——以太网协议、ARP协议

目录 以太网协议 以太网协议的简介 以太网协议所处的位置 以太网帧&#xff08;或者说MAC帧&#xff09;的格式 局域网通信原理 碰撞避免算法&#xff08;包含MTU的知识点&#xff09; 局域网攻击原理 ARP协议 ARP协议所在的位置 为什么要存在ARP协议&#xff08;或者…

nodejs669在线图书借阅管理系统vue前端

系统的设计与实现主要实现角色有管理员和用户,管理员在后台管理用户模块、用户表模块、图书借阅模块、图书归还模块、图书分类模块、token表模块、收藏表模块、书籍信息模块、图书资讯模块、留言板模块、书籍信息评论表模块、注册用户模块、配置文件模块、处罚记录模块、在线客…

Mysql更新Blob存储的Josn数据

Mysql更新blob存储的Josn数据 记录一次mysql操作blob格式存储的json字符串数据 1、检查版本 -- 版本5.7以上才可以能执行json操作 select version(); 2、创建测试数据 -- 创建测试表及测试数据 CREATE TABLE test_json_table AS SELECT UUID(), {"test1": {"…

Java---权限修饰符、final、static

文章目录 1. 权限修饰符2. final(最终态)3. static(静态) 1. 权限修饰符 修饰符同一个类中同一个包中的子类和无关类不同包的子类不同包的无关类private√默认√√protected√√√public√√√√ 2. final(最终态) 1. final关键字是最终的意思&#xff0c;可以修饰成员方法、…

OpenCV数字图像处理——检测出图像中的几何形状并测量出边长、直径、内角

一、简介 在传统的自动化生产尺寸测量中&#xff0c;常用的方法是利用卡尺或千分尺对被测工件的某个参数进行多次测量&#xff0c;并取这些测量值的平均值。然而&#xff0c;这些传统的检测设备或手动测量方法存在着一些问题&#xff1a;测量精度不高、测量速度缓慢&#xff0…

【单调栈】最大二叉树

题目&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums…

python 爬虫之 爬取网站信息并保存到文件

文章目录 前期准备探索该网页的HTML码的特点开始编写代码存入文件总的程序文件存储效果 前期准备 随便找个网站进行爬取&#xff0c;这里我选择的是(一个卖书的网站&#xff09; https://www.bookschina.com/24hour/62700000/ 我的目的是爬取这个网站的这个页面的书籍的名称以…

git打tag和版本控制规范

我们在开发中经常会遇到要打tag的情况&#xff0c;但这个tag应该如何打呢&#xff1f;我不知道大家平时是怎么打的&#xff0c;但我基本就是从1.0.0开始进行往上递增&#xff0c;至于如何递增&#xff0c;基本凭感觉。今天同事新打了一个tag进行发版&#xff0c;然后被架构点名…

佳易王个体诊所管理系统电子处方软件,个体诊所人员服务软件,卫生室配方模板电子病历系统教程

佳易王个体诊所管理系统电子处方软件&#xff0c;个体诊所人员服务软件&#xff0c;卫生室配方模板电子病历系统教程 软件试用版下载可以点击最下方官网卡片 软件功能&#xff1a; 1、配方模板&#xff1a;可以自由添加配方分类&#xff0c;预先设置药品配方&#xff0c;可以…

【赠书第9期】巧用ChatGPT高效搞定Excel数据分析

文章目录 前言 1 操作步骤 1.1 数据清理和整理 1.2 公式和函数的优化 1.3 图表和可视化 1.4 数据透视表的使用 1.5 条件格式化和筛选 1.6 数据分析技巧 1.7 自动化和宏的创建 2 推荐图书 3 粉丝福利 前言 ChatGPT 是一个强大的工具&#xff0c;可以为你提供在 Exce…

Mysql中的引擎介绍(InnoDB,MyISAM,Memory)

MySQL引擎就是指表的类型以及表在计算机上的存储方式。 MySQL数据库及其分支版本主要的存储引擎有三种&#xff0c;分别是 InnoDB、MyISAM、 Memory&#xff0c;还有一些其他的&#xff0c;CSV、Blackhole等&#xff0c;比较少见&#xff0c;可以使用SHOW ENGINES语句来查看。结…

蓝桥杯day02——第三大的数

题目 给你一个非空数组&#xff0c;返回此数组中 第三大的数 。如果不存在&#xff0c;则返回数组中最大的数。 示例 1&#xff1a; 输入&#xff1a;[3, 2, 1] 输出&#xff1a;1 解释&#xff1a;第三大的数是 1 。 示例 2&#xff1a; 输入&#xff1a;[1, 2] 输出&…

随时随地,打开浏览器即可体验的在线PS编辑器

即时设计 即时设计是国产的专业级 UI 设计工具&#xff0c;不限平台不限系统&#xff0c;在浏览器打开即用&#xff0c;能够具备 Photoshop 的设计功能&#xff0c;钢笔、矢量编辑、矩形工具、布尔运算等设计工具一应俱全&#xff0c;是能够在线使用的 Photoshop 免费永久工具…

【开发实践】网页预览excel表格原版样式

一、需求分析 由于业务部门需要&#xff0c;在导出excel表格页面&#xff0c;不需要先下载&#xff0c;就可以直接在页面上预览该表格文件。 二、代码实现 使用Luckysheet实现&#xff1a; 什么是Luckysheet Luckysheet &#xff0c;一款纯前端类似excel的在线表格&#xff0…

小程序如何进行一键修复

在使用小程序过程中&#xff0c;难免会遇到一些问题&#xff0c;比如程序崩溃、功能异常等等。这时&#xff0c;版本一键修复就显得尤为重要了。下面&#xff0c;我们就来介绍一下小程序如何进行版本一键修复。 一、什么是版本一键修复&#xff1f; 版本一键修复是指在小程序…

基于SSM的电商购物网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

Buzz库python代码示例

Buzz库来编写一个下载器程序。 php <?php require_once vendor/autoload.php; // 引入Buzz库 use Buzz\Browser; use Buzz\Message\Response; $browser new Browser(); // 设置 $browser->setHttpClient(new HttpClientProxy([ host > , port > , ])…