Net Core 使用Mongodb操作文件(上传,下载)

Net Core 使用Mongodb操作文件(上传,下载)

1.Mongodb GridFS 文件操作帮助类。

GridFS 介绍 https://baike.baidu.com/item/GridFS/6342715?fr=aladdin

DLL源码:https://gitee.com/chenjianhua1985/mongodb-client-encapsulation

DLL文件:链接:https://pan.baidu.com/s/1SpWDtaXjavalJQav89UE4A?pwd=yceu 提取码:yceu

/// <summary>/// MongoDB 文件 操作类/// </summary>public abstract class MongoFileRepository : IMongoFileRepository{#region GridFS 介绍// MongodB使用两个集合来存储GridFS文件,一个是fs.files,另一个是fs.chunks//  fs.files这个集合中存储的是每一个上传到数据库的文档的信息//  fs.chunks这个集合存储的是上传文件的内容。一个chunk相当于一个文档(大文件被拆分成多个有序的chunk)//  GridFS中的bucket这个概念指代的是fs.files和fs.chunks的组合// 它的工作原理是://   在GridFS存储文件是将文件分块存储,文件会按照256KB的大小分割成多个块进行存储//    GridFS使用两个集合//     (collection)存储文件,一个集合是chunks, 用于存储文件的二进制数据//      一个集合是files,用于存储文件的元数据信息(文件名称、块大小、上传时间等信息)//      从GridFS中读取文件要对文件的各各块进行组装、合并//  GridFSBucket 用于打开下载流对象#endregion/// <summary>/// 库 名/// </summary>public abstract string DataBaseName { get; }/// <summary>/// 配置类型/// </summary>public virtual DBConfigTypeEnum DBConfigType { get; set; } = DBConfigTypeEnum.WriteDB;/// <summary>///  根据 Id 获取内部信息/// </summary>/// <param name="id"></param>/// <returns></returns>public ObjectId GetInternalId(string id){if (!ObjectId.TryParse(id, out ObjectId internalId))internalId = ObjectId.Empty;return internalId;}/// <summary>///  根据 Id 获取文件 -- 异步/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<GridFSFileInfo> GetFileById(string id){// 通过系统 Id 筛选出包含 _id 的文件数据var filter = Builders<GridFSFileInfo>.Filter.Eq("_id", GetInternalId(id));return await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).Find(filter).FirstOrDefaultAsync();}/// <summary>/// 根据 Id 获取文件 -- 异步/// </summary>/// <param name="id">文件Id</param>/// <returns></returns>public async Task<GridFSFileInfo> GetFileById(ObjectId id){var filter = Builders<GridFSFileInfo>.Filter.Eq("_id", id);return await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).Find(filter).FirstOrDefaultAsync();}/// <summary>///  上传文件  -- 异步/// </summary>/// <param name="fileName">文件名称</param>/// <param name="source">流</param>/// <returns></returns>public async Task<ObjectId> UploadFile(string fileName, Stream source){var id = await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).UploadFromStreamAsync(fileName, source);return id;}/// <summary>///  根据Id 下载文件流可搜索  -- 异步/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<GridFSDownloadStream<ObjectId>> DownloadFileStreamSeekable(string id){var options = new GridFSDownloadOptions{Seekable = true};return await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).OpenDownloadStreamAsync(GetInternalId(id), options);}/// <summary>/// 根据Id 下载文件流可搜索 -- 异步/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<GridFSDownloadStream<ObjectId>> DownloadFileStreamSeekable(ObjectId id){var options = new GridFSDownloadOptions{Seekable = true};return await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).OpenDownloadStreamAsync(id, options);}/// <summary>/// 根据 Id 下载文件流 -- 异步/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<GridFSDownloadStream<ObjectId>> DownloadFileStream(string id){return await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).OpenDownloadStreamAsync(GetInternalId(id));}/// <summary>/// 下载 Id 获取文件流 -- 异步/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<GridFSDownloadStream<ObjectId>> DownloadFileStream(ObjectId id){return await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).OpenDownloadStreamAsync(id);}/// <summary>/// 根据Id删除文件 -- 异步/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task DeleteFile(string id){await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).DeleteAsync(GetInternalId(id));}/// <summary>/// 根据Id删除文件 -- 异步/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task DeleteFile(ObjectId id){await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).DeleteAsync(id);}/// <summary>/// 根据Id和文件名称移除文件 -- 异步/// </summary>/// <param name="id"></param>/// <param name="newFilename"></param>/// <returns></returns>public async Task RenameFile(string id, string newFilename){await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).RenameAsync(GetInternalId(id), newFilename);}/// <summary>///  根据Id和文件名称移除文件 --异步/// </summary>/// <param name="id"></param>/// <param name="newFilename"></param>/// <returns></returns>public async Task RenameFile(ObjectId id, string newFilename){await MongoDBClient.GetGridFSBucketInstance(DataBaseName, DBConfigType).RenameAsync(id, newFilename);}}

在项目的使用实例

本人比较喜欢封装好再用。所以将上面的帮助类封装成了一个DLL文件,这样在项目中可以直接引用。

 项目结构

 API层有一个mongoDBConfig.json这个是客户端连接配置文件。

[{"DbName": "PMFiles","ReadConnectionString": "mongodb://192.168.10.200:27017","WriteConnectionString": "mongodb://192.168.10.200:27017"},{"DbName": "Test","ReadConnectionString": "mongodb://192.168.10.200:27017","WriteConnectionString": "mongodb://192.168.10.200:27017"}
]

使用很简单,在要引用的类库中直接引用DLL文件就可以。

下面是具体的实例代码:

1.数据访问层

/// <summary>/// MongodbFile处理接口/// </summary>public interface IFileRepository : IMongoFileRepository{}

这里创建的接口是空的,没有要扩展的方法,常用的接口都在 IMongoFileRepository 基类接口中定义了.

实现类:

/// <summary>/// Mongodb 文件 数据库 操作类/// </summary>public class FileRepository : MongoFileRepository , IFileRepository{public override string DataBaseName => "PMFiles";}

这里创建的实现类也是空的,没有要扩展的方法,常用的接口都在 MongoFileRepository基类中实现了.

注意: 这里重写了DataBaseName这里一定要重写基类的数据库名称。

2.业务层

/// <summary>/// 文件 操作 业务逻辑层接口/// </summary>public interface IFileService : IMongoFileRepository{}

实现类

这里主要是调用数据访问层的实例来实现功能。

[AutoInject(typeof(IFileService), InjectType.Scope)]

这里的服务注入用的是自动注入。可以改成手动注册。手动注册时可以删除类上的 AutoInject 标签

/// <summary>/// 文件操作服务/// </summary>[AutoInject(typeof(IFileService), InjectType.Scope)]public class FileService : IFileService{private readonly IFileRepository _srviceFile = RepositoryIocFactory.GetRegisterImp<IFileRepository>();public Task DeleteFile(string id){return _srviceFile.DeleteFile(id);}public Task DeleteFile(ObjectId id){return _srviceFile.DeleteFile(id);}public Task<GridFSDownloadStream<ObjectId>> DownloadFileStream(string id){return _srviceFile.DownloadFileStream(id);}public Task<GridFSDownloadStream<ObjectId>> DownloadFileStream(ObjectId id){return _srviceFile.DownloadFileStream(id);}public Task<GridFSDownloadStream<ObjectId>> DownloadFileStreamSeekable(string id){return _srviceFile.DownloadFileStreamSeekable(id);}public Task<GridFSDownloadStream<ObjectId>> DownloadFileStreamSeekable(ObjectId id){return _srviceFile.DownloadFileStreamSeekable(id);}public Task<GridFSFileInfo> GetFileById(string id){return _srviceFile.GetFileById(id);}public Task<GridFSFileInfo> GetFileById(ObjectId id){return _srviceFile.GetFileById(id);}public ObjectId GetInternalId(string id){return _srviceFile.GetInternalId(id);}public Task RenameFile(string id, string newFilename){return _srviceFile.RenameFile(id, newFilename);}public Task RenameFile(ObjectId id, string newFilename){return _srviceFile.RenameFile(id, newFilename);}public Task<ObjectId> UploadFile(string fileName, Stream source){return _srviceFile.UploadFile(fileName, source);}}

1、API层的实现

AIP层主要的接口有 上传 和 根据ID下载文件,以文件流对象返回数据。

/// <summary>/// MongoDB文件上传/// </summary>public class FileUploadController : BaseApiController{private IFileService serviceFile;public FileUploadController(IFileService fileService){serviceFile = fileService;}/// <summary>/// 上传文件/// </summary>/// <returns></returns>[HttpPost, Route("UploadFileAsync")][DisableRequestSizeLimit][DisableFormValueModelBinding]public async Task<AjaxResultPageModel> UploadFileAsync(){var rspModel = new AjaxResultPageModel();var fileIds = new List<string>();//检查ContentTypeif (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)){rspModel.Warning("内容类型不能为空");}else{var _defaultFormOptions = new FormOptions();var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit);var reader = new MultipartReader(boundary, Request.Body);var section = await reader.ReadNextSectionAsync();while (section != null){//把Form的栏位內容逐一取出var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out ContentDispositionHeaderValue contentDisposition);if (hasContentDispositionHeader){//按文件和键值对分类处理if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition)){FileMultipartSection currentFile = section.AsFileSection();//存储文件到Mongovar id = await serviceFile.UploadFile(currentFile.FileName, section.Body);fileIds.Add(id.ToString());}}section = await reader.ReadNextSectionAsync();}rspModel.Success(fileIds);}return rspModel;}/// <summary>/// 下载文件/// </summary>[HttpGet,Route("Download")]public async Task<IActionResult> Download(string id){var fileInfo = await serviceFile.GetFileById(id);if (fileInfo == null){return NotFound();}else{return File(await serviceFile.DownloadFileStream(fileInfo.Id), "application/octet-stream", fileInfo.Filename);}}}

前端实现:

前端使用vue组件

<template><div class="home"><el-card class="box-card-top" shadow="never"><el-row :gutter="100"><el-col :span="2"><div class="user-avatar"><p class="user-avatar-text">{{ avatar }}</p></div></el-col><el-col :span="22"><div class="text"><h3>{{ holler }} {{ username }}</h3><p>桂树青春百里疆,鹧鸪啼彻午阴凉. 延平津上峰如削,剑去江空水自长.--剑道·尘心</p><p>活在当下,着眼未来.没有一往无前的觉悟.就不配握紧手中的双刀.空有大志,却没有实际行动.天下万般兵刃.唯有过往,伤人最深.如果真相带来痛苦.谎言只会雪上加霜.  --LOL·亚索</p></div></el-col></el-row><!-- <h2>首页界面</h2><div class="hello"> --><!-- <el-input v-model="user" type="text" /> --><!-- <div id="message" v-html="remsg"></div> --><!-- <div id="el-input"><el-input id="chatbox" @keyup.native.enter="handle"  type="textarea" :rows="1" placeholder="请输入内容" v-model="msg"></el-input></div><el-button size="small" style="display:inline-block;" icon="el-icon-s-promotion" type="suceess" @click="handle" plain></el-button> --><!-- </div> --></el-card><el-row :gutter="20"><el-col :span="4"><el-card class="box-card-center" shadow="never"><el-upload:action="action":file-list="modeList1":http-request="modeUpload1":multiple="true":before-remove="handleRemove1"><el-button size="small" type="primary">上传</el-button></el-upload><el-button @click="upload1">点击上传文件</el-button><br/><el-button @click="fileUpload">点击上传文件1</el-button></el-card></el-col><el-col :span="4"><el-card class="box-card-center" shadow="never"></el-card></el-col><el-col :span="4"><el-card class="box-card-center" shadow="never"></el-card></el-col></el-row></div></template><script>import axios from 'axios'import config from "../../config/index";import service from "../../api/UploadFiles/index";export default {data() {return {username: null,holler: "欢迎回来 — ",avatar: "",action: "", //上传文件的接口mode1: {},modeList1:[],fd1: new FormData()};},created() {this.username = JSON.parse(sessionStorage.getItem(config.localStorageKey)).userName;this.avatar = this.username.slice(0, 1);},methods: {modeUpload1: function(item) {// console.log(item.file);// this.mode1 = item.file// const isIMG =//   item.file.type === 'image/jpg' ||//   item.file.type === 'image/jpeg' ||//   item.file.type === 'image/png'// const isLt = item.file.size / 1024 / 2000 <= 1// if (!isIMG) {//   this.error='文件格式有误\n请上传后缀为jpg\\png的图片'// }// if (!isLt) {//   // console.log(file.size)//   this.error='上传头像图片大小不能超过500KB!'// }// if(isIMG&&isLt){this.fd1.append('files', item.file); //这里是自己把文件对象获取到,并存储起来//console.log("modeUpload1-> addFile:",item.file);//}},upload1: function() {console.log("upload1-> 11,files.length=",this.fd1.getAll('files').length);if(this.fd1.getAll('files').length===0){//formdata.getAll()返回的是一个file的数组(当然这里是你之前的参数得是文件)this.error='请先上传文件'}else {console.log("upload1-> post");axios.post('https://localhost:5001/adminapi/FileUpload/UploadFileAsync', this.fd1, { //headers: {'Content-Type': 'multipart/form-data'}}).then(response => {console.log("upload1->",response.data);})}},handleRemove1(file, fileList) {let files = this.fd1.getAll('files');this.fd1.delete('files');let len = files.lengthfor(var i=0;i<len;i++){if(files[i].uid!=file.uid){ //uid应该是可以唯一标志文件的,如果不能,就是我,会死this.fd1.append('files',files[i])}}},//上传方法fileUpload(uploader) {//let form = new FormData();//form.append("files", uploader.file);//console.log(this.fd1);//console.log("--------------------------");//console.log(uploader)service.uploadFlie(this.fd1// onUploadProgress: (progressEvent) => {//   //这一步是展示上传的进度条,不展示也行,根据自身需求决定//   let percent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;//   uploader.onProgress({ percent: percent }); //调用uploader的进度回调// },).then((res) => {console.log(res);if (res.success) {this.$message({message: "上传成功",type: "success",});} else {this.$message.error("上传失败,"+res.message);}}).catch((err) => {this.$message.error(err);});},},};/* import * as signalR from "@microsoft/signalr";let hubUrl = "https://localhost:44360/chatHub";//"https://localhost:44367/chatHub";//https://localhost:44318/chatHub; https://localhost:44367/chatHub//.net core 版本中默认不会自动重连,需手动调用 withAutomaticReconnectconst connection = new signalR.HubConnectionBuilder().withAutomaticReconnect().withUrl(hubUrl).build();connection.start().catch(err => alert(err.message));export default {name: "Im",mounted() {var _this = this;console.log('hubUrl:',hubUrl);//实现Show方法 OKconnection.on("Show", function(username, message) {_this.remsg = _this.remsg + "<br>" + username + ":" + message;console.log('Show:',message);});//实现ConnectResponse方法//connection.on("ChatHubReceiveMsg", function(username, message) {//  _this.remsg = _this.remsg + "<br>" + username + ":" + message;//  console.log('ChatHubReceiveMsg:',message);//}); //实现DisconnectResponse方法//connection.on("DisconnectResponse", function(username, message) {//  _this.remsg = _this.remsg + "<br>" + username + ":" + message;//  console.log('DisconnectResponse:',message);//});},data() {return {user: "cjh",msg: "",remsg: ""};},methods: {handle: function() {if(this.msg.trim()==""){alert("不能发送空白消息");return;}//调用后端方法 SendMsg 传入参数 OKconnection.invoke("SendMsg", this.user, this.msg);//connection.invoke("PublicSendMsg", this.msg);this.msg = "";}}}; */</script><style lang="less" scoped>.home {margin: 20px;.box-card-top {min-height: 160px;.user-avatar {width: 100px;height: 100px;margin-left: 50px;margin-top: 5px;border-radius: 50%;background-image: linear-gradient(#cdeefa, #b4b4fc);// background-color: #b4b4fc;.user-avatar-text {font-size: 40px;text-align: center;padding-top: 22px;color: #383838;}}.text h3 {margin-top: 20px;margin-bottom: 10px;font-size: 20px;}.text p {font-size: 16px;user-select: none;}}.box-card-center {width: 350px;height: 300px;margin-top: 20px;}}h1,h2 {font-weight: normal;}ul {list-style-type: none;padding: 0;}li {display: inline-block;margin: 0 10px;}a {color: #42b983;}#el-input {display: inline-block;width: 96%;float: left;}#message {overflow-y: auto;text-align: left;border: #42b983 solid 1px;height: 500px;}</style>

跨域问题处理。

requestFile.js

注意上图的类型只能是这个类型,只能一个类型,不能多。

//interceptors.js// vue axios配置 发起请求加载loading请求结束关闭loading
// http request 请求拦截器,有token值则配置上token值
import axios from 'axios'
import common from '../utils/common'
// import { API_BASE } from '../config/config';
// axios.defaults.baseURL = API_BASE;
// api base_url,设置前缀不存在
// const BASE_URL="";
const service = axios.create({baseURL: '', timeout: 60000, // 请求超时时间headers: {// Authorization: Authorization,'Content-Type': 'multipart/form-data'}
});
// Content-Type: application/wasmsql-wasm.wasm
// http请求拦截器
service.interceptors.request.use(config => {let token = common.getToken();//console.log('token:', token);if (token) {// bus.$emit('toggleloading', true)//显示loading// Loading.service(options);//如果token存在config.headers['Authorization'] = `Bearer ${token}`;//console.log('token 1:', token);}return config;},error => {Promise.reject(error);}
)// http response 服务器响应拦截器,
// 这里拦截401错误,并重新跳入登页重新获取token
service.interceptors.response.use(response => {if (response.status === 200) {//通讯成功// Toast.clear();// console.log(response);/************** response.data.status === 0 错误* response.data.status === 100 成功* response.data.status === 401 token过期** *************///  response.data.head.errorCode// bus.$emit('toggleloading', false)//隐藏loadingif (response.data.state == 401) {//未授权//如果是token过期,跳转至登录// console.log("401");//common.goLogin();message.error("未授权 请联系管理员!");//store.commit('setToken', '');} else if (response.data.state == 0) {// Message.error(response.data.message);return response.data;} else {// util.goLogin();return response.data;}}},error => {//请求失败// ;const response = error.response;if (response.status === 401) {// Toast.fail(response.data.message);message.error("未授权 请联系管理员!");//util.goLogin();} else if (response.status === 403) {$router.push({name: '403'});} else {// Toast.fail(response.data.message ? response.data.message : '系统错误请联系管理员');// message.error({//     message: '无服务,请联系管理员'// });}return Promise.reject(error);}
);export default service;

API js

import http from '../../utils/http'export default {// 上传文件uploadFileAsync:(params) => {return http.postMongoDBFile("adminapi/FileUpload/UploadFileAsync",params);},// 下载文件download:(params) => {return http.postMongoDBdownloadFile("adminapi/FileUpload/Download?id="+params)}
};

完成。

END

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

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

相关文章

STM32点亮LED灯与蜂鸣器发声

STM32之GPIO GPIO在输出模式时可以控制端口输出高低电平&#xff0c;用以驱动Led蜂鸣器等外设&#xff0c;以及模拟通信协议输出时序等。 输入模式时可以读取端口的高低电平或电压&#xff0c;用于读取按键输入&#xff0c;外接模块电平信号输入&#xff0c;ADC电压采集灯 GP…

论坛管理系统|基于Spring Boot+ Mysql+Java+B/S架构的论坛管理系统设计与实现(可运行源码+数据库+设计文档+部署说明+视频演示)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 目录 前台功能效果图 管理员功能登录前台功能效果图 用户功能模块 系统功能设计 数据库E-R图设计 l…

OxyPlot图表曲线图学习笔记(winform)

一、学习OxyPlot 开源地址&#xff1a;https://github.com/oxyplot/oxyplot 最新版&#xff1a;v2.1.2 新建winform&#xff0c;nuget中添加依赖包 二、写代码 2.1 BarSeries 2.2 ScatterSeries 2.3 LineSeries (带指向箭头&#xff09; int pointCount 50; double[] xs …

Xilinx 7系列FPGA的配置流程

目录 1.4配置流程 1.4.1 设备上电 ​编辑1.4.2 清除配置寄存器 1.4.3 采样模式引脚 1.4.4 同步 ​编辑1.4.5 检测设备ID ​编辑1.4.6 加载配置数据 1.4.7 CRC校验 1.4.8 启动序列 1.4配置流程 对于所有配置模式&#xff0c;7系列的基本配置流程都是相同的&…

C语言sizeof操作符与strlen函数

1.sizeof与strlen的介绍 (1).sizeof 计算变量的内存空间大小。底层实际上是对变量类型的计算。是一个单目操作符&#xff0c;不是函数&#xff0c;后面的括号可以省略 (2).strlen 函数原型 strlen是一个函数&#xff0c;返回的size_t类型的数据,头文件为string.h计算字符串…

Python爬虫:requests模块的基本使用

学习目标&#xff1a; 了解 requests模块的介绍掌握 requests的基本使用掌握 response常见的属性掌握 requests.text和content的区别掌握 解决网页的解码问题掌握 requests模块发送带headers的请求掌握 requests模块发送带参数的get请求 1 为什么要重点学习requests模块&…

如何在安卓端的SAP里配置打印机

1、转到 SPAD 事务&#xff08;“假脱机管理”屏幕&#xff09;。单击“设备/服务器”选项卡&#xff0c;输入输出设备的名称&#xff0c;然后点击显示按钮。 2、如果打印机已配置&#xff0c;它将显示设备属性、输出设备属性和纸盒信息的所有详细信息。如果输入新的打印机名称…

AndroidStudio设计登录页源码(音悦app)

目录 一、代码 二、效果 一、代码 1.在activity_main.xml里的代码 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent&quo…

import gdal 报错

1.下载gdal https://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal 2.安装正确版本 &#xff08;1&#xff09;查看python版本 python -v我的版本Python 3.7.9 建议下载 GDAL-3.4.2-cp37-cp37m-win_amd64.whl &#xff08;2&#xff09;放到Scripts文件夹下 执行 pip install GD…

mysql启动报错:ERROR! The server quit without updating PID file

mysql 重启或者启动时报错&#xff1a;ERROR! The server quit without updating PID file (/home/data/mysql/e787y33206.novalocal.pid).如下图: 重启报错&#xff1a; 启动也报错 这个错误是因为有mysql启动没有完全结束&#xff0c;如果是重启mysql服务&#xff0c;则在…

L2-035 完全二叉树的层序遍历(Python)

L2-035 完全二叉树的层序遍历 分数 25 全屏浏览 切换布局 作者 陈越 单位 浙江大学 一个二叉树&#xff0c;如果每一个层的结点数都达到最大值&#xff0c;则这个二叉树就是完美二叉树。对于深度为 D 的&#xff0c;有 N 个结点的二叉树&#xff0c;若其结点对应于相同深度…

超详细——VsCode连接远程主机/虚拟机并设置免密登录

超详细——VsCode连接远程主机/虚拟机并设置免密登录 文章目录 超详细——VsCode连接远程主机/虚拟机并设置免密登录[toc]连接到远程主机/虚拟机步骤1、打开vscode拓展 搜索 remote-ssh并安装步骤2、打开远程主机/虚拟机的ssh服务步骤3、设置连接远程主机的配置文件 设置免密登…

机械女生,双非本985硕,目前学了C 基础知识,转嵌入式还是java更好?

作为单片机项目开发的卖课佬&#xff0c;个人建议&#xff0c;先转嵌入式单片机开发方向&#xff0c;哈哈。 java我也学过&#xff0c;还学过oracle、mysql数据库&#xff0c;只是当时没做笔记&#xff0c;找不好充分的装逼证据了。 从实习通过业余时间&#xff0c;学到快正式毕…

可以用SEO进行市场分析吗?(川圣SEO)蜘蛛池

baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; SEO如何去进行关键词市场占比分析呢&#xff1f;可以进行市场分析吗&#xff1f; SEO&#xff0c;即搜索引…

Linux学习:权限

目录 1. shell命令的工作原理与存在意义1.1 shell命令解释器存在的意义1.2 shell解释器的工作原理 2. Linux操作系统&#xff1a;用户2.1 什么是用户2.2 用户的切换操作2.3 用户权限划分的意义 3. Linux中权限的种类和意义3.1 什么是权限3.2 sudo指令与短暂提权 4. 文件类型与文…

嵌入式3-12

1.整理思维导图 2.把上课没做完的结构体大小求完(写出过程) 3.把枚举部分的练习题&#xff0c;再做一遍 4.三种验证大小端存储大的代码写一遍 1.思维导图在最后 2.结构体大小 struct data{ 1233000044444444&#xff0c;16字节char t1; char t2; unsigned …

【PLIO学习总结】laserMapping中的时间戳与状态更新逻辑

本文仅用于个人学习总结记录。如有错误&#xff0c;请批评指正。 0、PLIO简要思路 从PLIO的论文中&#xff0c;可以知道&#xff0c;完整的PLIO算法采用IMU和LiDAR数据同时作为“输入”&#xff0c;维护状态变量包括加速度和角速度。 同时&#xff0c;PLIO是一种distortion-…

springboot266基于Web的农产品直卖平台的设计与实现

农产品直卖平台的设计与实现 摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#x…

C#构造函数

C#中的构造函数是一种特殊的方法&#xff0c;用于创建和初始化类的对象。构造函数的名称与类的名称相同&#xff0c;并且没有返回类型。 在C#中&#xff0c;构造函数有以下几种类型&#xff1a; 默认构造函数&#xff1a;如果在类中没有定义构造函数&#xff0c;系统将自动提供…

二、HarmonyOS 操作系统以及相关生态

前言 2019年8月9日&#xff0c;华为技术有限公司在华为开发者大会上正式发布了HarmonyOS 1.0&#xff0c;同时宣布该操作系统源代码开源。 2020年9月10日&#xff0c;HarmonyOs 2.0正式发布。与HarmonyOs 1.0版本相比&#xff0c;HarmonyOs 2.0在分布式软总线、分布式数据管理、…