环信鸿蒙IM SDK实现附件消息发送与下载

环信HarmonyOS IM SDK 正式版已经发布,该版本全面覆盖即时通讯(IM)的核心功能,为用户提供了完整的IM全功能体验,同时支持从Android APK到 NEXT 的数据迁移,更好地满足企业在不同业务场景下的适配需求。

点此下载最新版环信 HarmonyOS IM SDK

环信鸿蒙IM SDK 除文本消息外,还支持发送附件类型消息,包括语音、图片、视频和文件消息。本文为大家介绍从系统获取系统图片,视频,语音文件,使用sdk进行发送与下载。

一、发送附件消息
1、发送图片消息

① 权限添加
在对应模块的module.json5配置中,添加requestPermissions节点和对应权限的配置:

{"name": "ohos.permission.READ_MEDIA","reason": "$string:app_name","usedScene": {"abilities": ["EntryAbility"]}
},
{"name": "ohos.permission.WRITE_MEDIA","reason": "$string:app_name","usedScene": {"abilities": ["EntryAbility"]}
}

② 权限检测

//检查权限
export  async function checkAppPermission(context:common.UIAbilityContext): Promise<boolean> {try {const READ_MEDIA_PERMISSION: Permissions = 'ohos.permission.READ_MEDIA' //媒体读取权限const WRITE_MEDIA_PERMISSION: Permissions = 'ohos.permission.WRITE_MEDIA' //媒体写入权限let permissionList: Permissions[] = []; //需要申请选项列表let readPermission = await checkPermissions(READ_MEDIA_PERMISSION)//检查是否有媒体读取权限!readPermission && permissionList.push(READ_MEDIA_PERMISSION)let writePermission = await checkPermissions(WRITE_MEDIA_PERMISSION)//检查是否有媒体写入权限!writePermission && permissionList.push(READ_MEDIA_PERMISSION)if (permissionList.length) {//申请权限let res: boolean = await applyPermission(context, permissionList)if (!res) {//用户未同意授权AlertDialog.show({title: "提示",message: "无权限读写用户外部存储中的媒体文件信息,请前往系统设置开启",alignment: DialogAlignment.Center,secondaryButton: {value: '关闭',action: () => {}}})
}
return res
}
return true}catch (e) {return Promise.reject(e)
}
}

检查用户权限:

//检查用户权限
//@params permissions:权限名称数组
export  async function checkPermissions(permissions: Permissions): Promise<boolean> {try {let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions);return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED}catch (e) {return Promise.reject(e)}
}

从相册中选择图片:

 //从相册选择let PhotoSelectOptions = new picker.PhotoSelectOptions();PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;PhotoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();photoPicker.select(PhotoSelectOptions).then(async (PhotoSelectResult) => {if (PhotoSelectResult.photoUris.length) {//复制图片到缓存目录(缓存目录才有读写权限)let filePath = PhotoSelectResult.photoUris[0]if (filePath) {//这里获取到的filePath 就是图片的路径}}

发送方调用 createImageSendMessage 方法传入图片的本地资源标志符 URI、设置是否发送原图以及接收方的用户 ID (群聊或聊天室分别为群组 ID 或聊天室 ID)创建图片消息,然后调用 sendMessage 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。

这里imageFilePathOrUri 为上面获取到的filePath
// `imageFilePathOrUri` 为图片本地路径或者Uri。
let message = ChatMessage.createImageSendMessage(toChatUsername, imageFilePathOrUri);
// 会话类型,包含 `Chat`、`GroupChat` 和 `ChatRoom`,表示单聊、群聊或聊天室,默认为单聊。
message.setChatType(ChatType.GroupChat);
// 发送消息
ChatClient.getInstance().chatManager()?.sendMessage(message);
2、发送视频消息

① 缩略图获取

import picker from '@ohos.file.picker';
import {checkAppPermission} from '../../utils/permissions/CheckAccess'
import { common } from '@kit.AbilityKit';
import camerapicker from '@ohos.multimedia.cameraPicker';
import camera from '@ohos.multimedia.camera';
import { BusinessError } from '@ohos.base';
import { ChatMessage, } from 'easemob'
import  SendMessages from '../../presenter/SendMessage'
//全局公共样式
@Styles
function fillScreen() {.width('100%').backgroundColor(Color.White).borderRadius(20).padding(10)
}@CustomDialog
@Component
export struct AttachmentMessageDialog {@Prop conversationId: ResourceStr@Prop imgMsg: ChatMessage|undefined = undefinedchangeMessage = (imgMsg: ChatMessage|undefined)=> {}private controller: CustomDialogControllerprivate context = getContext(this) as common.UIAbilityContext; //UIAbilityContextbuild() {Column() {Row(){Image($r('app.media.ease_chat_takepic_pressed')).width(20).height(20)Text("相机").fontColor('#33b1ff')}.width('100%').padding({top:30,left:20,bottom:20}).onClick(async ()=>{this.camera()})Row(){}.width('100%').backgroundColor("#e7e9eb").height(1)Row(){Image($r('app.media.ease_chat_image_pressed')).width(20).height(20)Text("相册").fontColor('#33b1ff')}.width('100%').padding({top:20,left:20,bottom:20}).onClick(async () =>{this.photo()})Row(){}.width('100%').backgroundColor("#e7e9eb").height(1)Row(){Image($r('app.media.em_chat_video_pressed')).width(20).height(20)Text("视频").fontColor('#33b1ff')}.width('100%').padding({top:20,left:20,bottom:20}).onClick(async ()=>{ this.video()})Row(){}.width('100%').backgroundColor("#e7e9eb").height(1)Row(){Image($r('app.media.em_chat_card_normal')).width(20).height(20)Text("名片").fontColor('#33b1ff')}.width('100%').padding({top:20,left:20,bottom:20}).onClick( ()=>{})Row(){}.width('100%').backgroundColor("#e7e9eb").height(7)Row(){Text("取消").fontColor('#33b1ff')}.padding({top:20,left:20,bottom:20})}.width('100%')}private async photo(){//检查是否有读写外部媒体权限let res: boolean = await checkAppPermission(this.context)//无权限返回if (!res) return//从相册选择let PhotoSelectOptions = new picker.PhotoSelectOptions();PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;PhotoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();photoPicker.select(PhotoSelectOptions).then(async (PhotoSelectResult) => {if (PhotoSelectResult.photoUris.length) {//复制图片到缓存目录(缓存目录才有读写权限)let filePath = PhotoSelectResult.photoUris[0]if (filePath) {SendMessages.sendImageMessage(this.conversationId+"", filePath).then((value)=>{this.changeMessage(value)})}}})}private async camera(){//检查是否有读写外部媒体权限let res: boolean = await checkAppPermission(this.context)//无权限返回if (!res) returntry {let pickerProfile: camerapicker.PickerProfile = {cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK};let pickerResult: camerapicker.PickerResult = await camerapicker.pick(this.context,[camerapicker.PickerMediaType.PHOTO, camerapicker.PickerMediaType.PHOTO], pickerProfile);if(pickerResult?.resultUri){}} catch (error) {let err = error as BusinessError;console.error(`the pick call failed. error code: ${err.code}`);}}private async video(){console.log(this.conversationId+"------")//检查是否有读写外部媒体权限let res: boolean = await checkAppPermission(this.context)//无权限返回if (!res) return//从相册选择let PhotoSelectOptions = new picker.PhotoSelectOptions();PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.VIDEO_TYPE;PhotoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();photoPicker.select(PhotoSelectOptions).then(async (PhotoSelectResult) => {if (PhotoSelectResult.photoUris.length) {//复制图片到缓存目录(缓存目录才有读写权限)let filePath = PhotoSelectResult.photoUris[0]if (filePath) {SendMessages.sendVideoMessage(this.conversationId+"", filePath,this.context).then((value)=>{this.changeMessage(value)})console.log("PhotoSelectOptions",filePath)}}})}
}

②发送方调用 createVideoSendMessage 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)、视频文件的本地路径、视频时长以及缩略图的本地存储路径,然后调用 sendMessage 方法发送消息。SDK 会将视频文件上传至消息服务器。若需要视频缩略图,你需自行获取视频首帧的路径,将该路径传入 createVideoSendMessage 方法。
上面获取到视频首诊

// 在应用层获取视频首帧
let thumbPath = this.getThumbPath(videoPath);
let message = ChatMessage.createVideoSendMessage(toChatUsername, videoPath, videoLength, thumbPath);
if (!message) {return;
}
// 会话类型,包含 `Chat`、`GroupChat` 和 `ChatRoom`,表示单聊、群聊或聊天室,默认为单聊。
message.setChatType(ChatType.GroupChat);
// 发送消息
ChatClient.getInstance().chatManager()?.sendMessage(message);
3、发送语音消息

①音频录制

// 音频录制
import { media } from '@kit.MediaKit';
import { BusinessError, systemDateTime } from '@kit.BasicServicesKit';
import fs from '@ohos.file.fs';
class AudioRecorder {private avRecorder: media.AVRecorder | undefined = undefined;public maxAmplitude: number = 0;public audioPath = ""private avProfile: media.AVRecorderProfile = {audioBitrate: 100000, // 音频比特率audioChannels: 2, // 音频声道数audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式,当前只支持aacaudioSampleRate: 48000, // 音频采样率fileFormat: media.ContainerFormatType.CFT_MPEG_4A, // 封装格式,当前只支持m4a};private avConfig: media.AVRecorderConfig = {audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频输入源,这里设置为麦克风profile: this.avProfile,url: 'fd://35', // 参考应用文件访问与管理开发示例新建并读写一个文件};// 注册audioRecorder回调函数setAudioRecorderCallback(): void {if (this.avRecorder !== undefined) {// 状态机变化回调函数this.avRecorder.on('stateChange', (state: media.AVRecorderState, _: media.StateChangeReason) => {console.log(`AudioRecorder current state is ${state}`);})// 错误上报回调函数this.avRecorder.on('error', (err: BusinessError) => {console.error(`AudioRecorder failed, code is ${err.code}, message is ${err.message}`);})}}// 开始录制对应的流程async startRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined) {await this.avRecorder.release();this.avRecorder = undefined;}// 1.创建录制实例this.avRecorder = await media.createAVRecorder();this.setAudioRecorderCallback();// 2.获取录制文件fd赋予avConfig里的url;参考FilePicker文档const context = getContext(this);const path = context.filesDir;let currentTimeMillis: number = new Date().getTime();const filepath = path + '/' + currentTimeMillis + '.wav';this.audioPath = filepathconst file = fs.openSync(filepath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);const fdNumber = file.fd;this.avConfig.url = 'fd://' + fdNumber;// 3.配置录制参数完成准备工作await this.avRecorder.prepare(this.avConfig);// 4.开始录制await this.avRecorder.start();}// 暂停录制对应的流程async pauseRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined && this.avRecorder.state === 'started') { // 仅在started状态下调用pause为合理状态切换await this.avRecorder.pause();}}// 恢复录制对应的流程async resumeRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined && this.avRecorder.state === 'paused') { // 仅在paused状态下调用resume为合理状态切换await this.avRecorder.resume();}}// 停止录制对应的流程async stopRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined) {// 1. 停止录制if (this.avRecorder.state === 'started'|| this.avRecorder.state === 'paused') { // 仅在started或者paused状态下调用stop为合理状态切换await this.avRecorder.stop();}// 2.重置await this.avRecorder.reset();// 3.释放录制实例await this.avRecorder.release();this.avRecorder = undefined;}}
}let audioRecorder = new AudioRecorder()
export default audioRecorder as AudioRecorder

②发送方调用 createVoiceSendMessage 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)、语音文件的路径和语音时长创建语音消息,然后调用 sendMessage 方法发送消息。SDK 会将语音文件上传至环信服务器

// `filePathOrUri` 为语音文件的本地路径或者文件的 URI,`duration` 为语音时长(单位为秒)。
let message = ChatMessage.createVoiceSendMessage(to, filePathOrUri, duration);
if (!message) {return;
}
// 设置会话类型,即`ChatMessage` 类的 `ChatType` 属性,包含 `Chat`、`GroupChat` 和 `ChatRoom`,表示单聊、群聊或聊天室,默认为单聊。
message.setChatType(ChatType.GroupChat);
// 发送消息
ChatClient.getInstance().chatManager()?.sendMessage(message);
二、接收附件消息

1、接收语音文件
接收方收到 onMessageReceived 回调,调用 getRemoteUrl 或 getLocalPath 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。

let voiceBody = message.getBody() as VoiceMessageBody;
// 获取语音文件在服务器的地址。
let voiceRemoteUrl = voiceBody.getRemoteUrl();
// 本地语音文件的本地路径。
let voiceLocalPath = voiceBody.getLocalPath();

2、接收图片消息
接收方收到图片消息,自动下载图片缩略图。
SDK 默认自动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 true。若设置为手动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 false ,需调用 ChatClient.getInstance().chatManager()?.downloadThumbnail(message) 下载。
接收方收到 onMessageReceived 回调,调用 downloadAttachment 下载原图。

let msgListener: ChatMessageListener = {onMessageReceived: (messages: ChatMessage[]): void => {messages.forEach( message => {if (message.getType() === ContentType.IMAGE) {let callback: ChatCallback = {onSuccess: (): void => {// 附件下载成功},onError: (code: number, error: string): void => {// 附件下载失败},onProgress: (progress: number): void => {// 附件下载进度}}message.setMessageStatusCallback(callback);// 下载附件ChatClient.getInstance().chatManager()?.downloadAttachment(message);}})}
}

3、接收视频消息
接收方收到视频消息时,自动下载视频缩略图。
SDK 默认自动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 true 。若设置为手动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 false,需调用 ChatClient.getInstance().chatManager()?.downloadThumbnail(message) 下载。
接收方收到 onMessageReceived 回调,可以调用 ChatClient.getInstance().chatManager()?.downloadAttachment(message) 方法下载视频原文件

let msgListener: ChatMessageListener = {onMessageReceived: (messages: ChatMessage[]): void => {messages.forEach( message => {if (message.getType() === ContentType.VIDEO) {let callback: ChatCallback = {onSuccess: (): void => {// 附件下载成功},onError: (code: number, error: string): void => {// 附件下载失败},onProgress: (progress: number): void => {// 附件下载进度}}message.setMessageStatusCallback(callback);// 下载附件ChatClient.getInstance().chatManager()?.downloadAttachment(message);}})}
}

获取视频缩略图和视频原文件。

let body = message.getBody() as VideoMessageBody;
// 从服务器端获取视频文件。
let imgRemoteUrl = body.getRemoteUrl();
// 从服务器获取视频缩略图文件。
let thumbnailUrl = body.getThumbnailRemoteUrl();
// 从本地获取视频文件文件。
let localPath = body.getLocalPath();
// 从本地获取视频缩略图文件。
let localThumbPath = body.getThumbnailLocalPath();

参考文档:

  • 注册环信IM:https://console.easemob.com/user/register

  • 环信官方Demo下载:https://www.easemob.com/download/demo

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

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

相关文章

[二维前缀和]最大纯色正方形

题目描述 铺砖的工人来到一个操场&#xff0c;将整个操场按正方形铺砖&#xff08;整个操场可视为R行C列的矩阵&#xff0c;矩阵的每个元素为一块正方形砖块&#xff09;&#xff0c;正方形砖块有两种&#xff0c;一种为蓝色&#xff0c;另一种为红色。请你写一个程序求出为最…

每日OJ题_牛客_最长上升子序列(二)_贪心+二分_C++_Java

目录 牛客_最长上升子序列(二)_贪心二分 题目解析 C代码 Java代码 牛客_最长上升子序列(二)_贪心二分 最长上升子序列(二)_牛客题霸_牛客网 (nowcoder.com) 描述&#xff1a; 给定一个长度为 n 的数组a&#xff0c;求它的最长严格上升子序列的长度。 所谓子序列&#xf…

使用uniapp + Vue3 + uni.createInnerAudioContext()实现播放歌曲及歌词滚动、拖动进度条

一、大致效果 二、使用步骤 1.歌词详情页代码块 <template><view class"play"><view class"play_centent" :style"{ background-image: url( playInfo.siPic ) }"><div class"cover-mask" style"opacit…

无人机维护保养、部件修理更换技术详解

无人机作为一种精密的航空设备&#xff0c;其维护保养和部件修理更换是确保飞行安全、延长使用寿命的重要环节。以下是对无人机维护保养、部件修理更换技术的详细解析&#xff1a; 一、无人机维护保养技术 1. 基础构造理解&#xff1a; 熟悉无人机的基本构造&#xff0c;包括…

解决Redis缓存穿透(缓存空对象、布隆过滤器)

文章目录 背景代码实现前置实体类常量类工具类结果返回类控制层 缓存空对象布隆过滤器结合两种方法 背景 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库 常见的解决方案有两种&#xff0c;分别…

【运动的&足球】足球场景目标检测系统源码&数据集全套:改进yolo11-ASF-P2

改进yolo11-RetBlock等200全套创新点大全&#xff1a;足球场景目标检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.11.03 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或…

【STM32】GPIO通用输入输出口

文章目录 一、GPIO的概念二、STM32中GPIO的基本结构三、GPIO位结构输入部分分析输出部分分析GPIO的8种模式 四、GPIO相关函数 一、GPIO的概念 GPIO&#xff08;General Purpose Input Output&#xff09;&#xff0c;意为通用输入输出口&#xff0c;在嵌入式系统中&#xff0c;…

stm32疑难杂症之电压不够程序跑飞

在开发阶段&#xff0c;如果出现程序不断重启的情况。 首先先检查是不是代码问题。 1.数组越界。定义的数组只有50个单元&#xff0c;但是程序运行的过程中&#xff0c;却给他赋给50个单元。 2.中断耗时太长&#xff0c;刚出中断又进去了。这时的现象是程序一直在中断中执行…

华为荣耀曲面屏手机下面空白部分设置颜色的方法

荣耀部分机型下面有一块空白区域&#xff0c;如下图红框部分 设置这部分的颜色需要在themes.xml里面设置navigationBarColor属性 <item name"android:navigationBarColor">android:color/white</item>

电子电气架构 --- 整车控制系统

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所有人的看法和评价都是暂时的&#xff0c;只有自己的经历是伴随一生的&#xff0c;几乎所有的担忧和畏惧…

STM32 HAL库 SPI驱动1.3寸 OLED屏幕

目录 参考硬件引脚与接线 点亮屏幕CubeMX 配置OLED 驱动程序代码 参考 基于STM32F103C8T6最小系统板HAL库CubeMX SPI驱动7针 OLED显示屏&#xff08;0.96寸 1.3寸通用&#xff09;0.96 oled HAL库驱动 SPI STM32SPI驱动0.96/1.3寸 OLED屏幕&#xff0c;易修改为DMA控制STM32驱…

iOS 18.2 可让欧盟用户删除App Store、Safari、信息、相机和照片应用

升级到 iOS 18.2 之后&#xff0c;欧盟的 iPhone 用户可以完全删除一些核心应用程序&#xff0c;包括 App Store、Safari、信息、相机和 Photos 。苹果在 8 月份表示&#xff0c;计划对其在欧盟的数字市场法案合规性进行更多修改&#xff0c;其中一项更新包括欧盟用户删除系统应…

力扣11.2

2742. 给墙壁刷油漆 给你两个长度为 n 下标从 0 开始的整数数组 cost 和 time &#xff0c;分别表示给 n 堵不同的墙刷油漆需要的开销和时间。你有两名油漆匠&#xff1a; 一位需要 付费 的油漆匠&#xff0c;刷第 i 堵墙需要花费 time[i] 单位的时间&#xff0c;开销为 cost…

[前端] 为网站侧边栏添加搜索引擎模块

前言 最近想给我的个人网站侧边栏添加一个搜索引擎模块&#xff0c;可以引导用户帮助本站SEO优化&#xff08;让用户可以通过点击搜索按钮完成一次对本人网站的搜索&#xff0c;从而实现对网站的搜索引擎优化&#xff09;。 最开始&#xff0c;我只是想实现一个简单的百度搜索…

nodejs入门教程20:nodejs文件系统

1. 引入fs模块 在Node.js中&#xff0c;你可以通过以下方式引入fs模块&#xff1a; const fs require(fs);2. 文件操作 读取文件 异步读取&#xff1a;使用fs.readFile()方法。该方法接收文件路径和回调函数作为参数&#xff0c;回调函数在文件读取完成后被调用。 fs.rea…

使用AWS Redshift从AWS MSK中读取数据

Amazon Redshift 流式摄取的目的是简化将流式数据直接从流式服务摄取到 Amazon Redshift 或 Amazon Redshift Serverless 的过程。 官方文档[1]中有详细步骤。用unauthenticated, IAM 的方式均可以进行连接&#xff0c;只不过使用的是不同端口&#xff1a;9092或者9098 [1] h…

2-3-4树的层序打印

234树 接前面0到100与B树比较0到50镜像代码更新随机测试后话接前面 红黑树转2-3-4树打印失败的地方。 0到100 这个是应该的样子 >>> rb = RBT() >>> for k in range(100):rb.INSERT(k) >>> t=tree234(rb.root) >>> t.print() 结点类型…

了解AIGC——文本生成技术在新闻媒体中的应用

了解AIGC&#xff1a;文本生成技术在新闻媒体中的应用 引言 生成式人工智能内容&#xff08;AI-Generated Content&#xff0c;简称AIGC&#xff09;在新闻媒体中的应用已经成为技术革新和内容创作的主要方向之一。新闻行业长期依赖于人类记者和编辑的创造力&#xff0c;而AI…

UBUNTU查看CPU核心数

UBUNTU查看CPU核心数 前言一、使用lscpu命令1. 执行命令2. 查看输出 二、使用/proc/cpuinfo文件1. 查看文件2. 解释输出 三、使用nproc命令1. 执行命令2. 查看输出 四、使用htop或top工具1. 使用htop2. 使用top 五、使用inxi命令1. 执行命令2. 查看输出 六、使用图形界面工具1.…

机器学习线性回归

文章目录 1.基本概念2.最小二乘法3.用代码来表示线性回归 1.基本概念 线性回归是一种统计方法&#xff0c;用于分析两个或多个变量之间的关系。其主要目的是通过建立一个线性模型来预测一个因变量&#xff08;或称为目标变量&#xff09;与一个或多个自变量&#xff08;或称为…