NBlog整合OSS图库

NBlog部署维护流程记录(持续更新):https://blog.csdn.net/qq_43349112/article/details/136129806

由于项目是fork的,所以我本身并不清楚哪里使用了图床,因此下面就是我熟悉项目期间边做边调整的。

目前已经调整的功能点:

  • QQ头像存储
  • 后端管理-图床管理

!更改配置和写代码的时候,注意不要把敏感信息上传到git仓库!

不小心上传了配置文件,又搞了好久。。。

如果已经上传了,可以参考这个博客进行处理:git 删除历史提交中的某个文件,包含所有记录,过滤所有记录_git filter-branch --index-filter "git rm -rf --cac-CSDN博客

image-20240310191935850

1.QQ头像存储

1.1 修改配置

修改配置文件applicaiton-dev.properties

# 评论中QQ头像存储方式: 本地:local GitHub:github 又拍云:upyun 阿里云:aliyun
upload.channel=aliyun

# 阿里云OSS
upload.aliyun.endpoint=oss-cn-shanghai.aliyuncs.com
upload.aliyun.bucket-name=chaobk-img-repo
upload.aliyun.path=cblog-qq
upload.aliyun.access-key-id=LTAI5tMbd4PzFjzLkPMssheu
upload.aliyun.secret-access-key=xxxxxxxxxxxxxxxxxxxxxxxxxx

1.2 新增实体类

新加入实体类,用来存放配置的值:

package com.chaobk.config.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "upload.aliyun")
public class AliyunProperties {
    private String endpoint;
    private String bucketName;
    private String path;
    private String accessKeyId;
    private String secretAccessKey;
}

1.3 修改bean工厂

修改上传方式bean生成器,即新增的第四个case块:

package com.chaobk.util.upload.channel;

import com.chaobk.constant.UploadConstants;
import com.chaobk.util.common.SpringContextUtils;

/**
 * 文件上传方式
 *
 * @author: Naccl
 * @date: 2022-01-23
 */
public class ChannelFactory {
	/**
	 * 创建文件上传方式
	 *
	 * @param channelName 方式名称
	 * @return 文件上传Channel
	 */
	public static FileUploadChannel getChannel(String channelName) {
		switch (channelName.toLowerCase()) {
			case UploadConstants.LOCAL:
				return SpringContextUtils.getBean(LocalChannel.class);
			case UploadConstants.GITHUB:
				return SpringContextUtils.getBean(GithubChannel.class);
			case UploadConstants.UPYUN:
				return SpringContextUtils.getBean(UpyunChannel.class);
			case UploadConstants.ALIYUN:
				return SpringContextUtils.getBean(AliyunChannel.class);
		}
		throw new RuntimeException("Unsupported value in [application.properties]: [upload.channel]");
	}
}

1.4 新增上传类

添加上传的操作类:

package com.chaobk.util.upload.channel;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectRequest;
import com.chaobk.config.properties.AliyunProperties;
import com.chaobk.util.upload.UploadUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.util.UUID;

/**
 * 阿里云OSS存储上传
 */
@Lazy
@Component
public class AliyunChannel implements FileUploadChannel {

    private AliyunProperties aliyunProperties;

    private OSS ossClient;

    public AliyunChannel(AliyunProperties aliyunProperties) {
        this.aliyunProperties = aliyunProperties;
        this.ossClient = new OSSClientBuilder().build(aliyunProperties.getEndpoint(), aliyunProperties.getAccessKeyId(), aliyunProperties.getSecretAccessKey());
    }

    @Override
    public String upload(UploadUtils.ImageResource image) throws Exception {
        String uploadName = aliyunProperties.getPath() + "/" + UUID.randomUUID() + "." + image.getType();
        PutObjectRequest putObjectRequest = new PutObjectRequest(aliyunProperties.getBucketName(), uploadName, new ByteArrayInputStream(image.getData()));
        try {
            ossClient.putObject(putObjectRequest);
            return String.format("https://%s.%s/%s", aliyunProperties.getBucketName(), aliyunProperties.getEndpoint(), uploadName);
        } catch (Exception e) {
            throw new RuntimeException("阿里云OSS上传失败");
        } finally {
            putObjectRequest.getInputStream().close();
        }
    }
}

后端处理完成,进行测试

1.5 测试

测试前需要先确保redis中没有要测试账号的缓存数据。

首先在博客页面填写评论:

image-20240310184826974

评论成功,路径正确,完成。

image-20240310185253969

2.后端管理-图床管理

纯前端代码。

主要是参考着官方的文档操作:安装和使用OSS Node.js SDK_对象存储(OSS)-阿里云帮助中心 (aliyun.com)

2.1 安装OSS

npm install ali-oss --save

2.2 调整setting.vue

照猫画虎,新增方法、aliyunConfig对象以及各种标签

<template>
  <div>
    <el-alert title="图床配置及用法请查看:https://github.com/Naccl/PictureHosting" type="warning" show-icon
              v-if="hintShow"></el-alert>
    <el-card>
      <div slot="header">
        <span>GitHub配置</span>
      </div>
      <el-row>
        <el-col>
          <el-input placeholder="请输入token进行初始化" v-model="githubToken" :clearable="true"
                    @keyup.native.enter="searchGithubUser" style="min-width: 500px">
            <el-button slot="append" icon="el-icon-search" @click="searchGithubUser">查询</el-button>
          </el-input>
        </el-col>
      </el-row>
      <el-row>
        <el-col>
          <span class="middle">当前用户:</span>
          <el-avatar :size="50" :src="githubUserInfo.avatar_url">User</el-avatar>
          <span class="middle">{{ githubUserInfo.login }}</span>
        </el-col>
      </el-row>
      <el-row>
        <el-col>
          <el-button type="primary" size="medium" icon="el-icon-check" :disabled="!isGithubSave"
                     @click="saveGithub(true)">保存配置
          </el-button>
          <el-button type="info" size="medium" icon="el-icon-close" @click="saveGithub(false)">清除配置</el-button>
        </el-col>
      </el-row>
    </el-card>

    <el-card>
      <div slot="header">
        <span>又拍云存储配置</span>
      </div>
      <el-form :model="upyunConfig" label-width="100px">
        <el-form-item label="操作员名称">
          <el-input v-model="upyunConfig.username"></el-input>
        </el-form-item>
        <el-form-item label="操作员密码">
          <el-input v-model="upyunConfig.password"></el-input>
        </el-form-item>
        <el-form-item label="存储空间名">
          <el-input v-model="upyunConfig.bucketName"></el-input>
        </el-form-item>
        <el-form-item label="CDN访问域名">
          <el-input v-model="upyunConfig.domain"></el-input>
        </el-form-item>
        <el-button type="primary" size="medium" icon="el-icon-check" :disabled="!isUpyunSave" @click="saveUpyun(true)">
          保存配置
        </el-button>
        <el-button type="info" size="medium" icon="el-icon-close" @click="saveUpyun(false)">清除配置</el-button>
      </el-form>
    </el-card>

    <el-card>
      <div slot="header">
        <span>腾讯云存储配置</span>
      </div>
      <el-form :model="txyunConfig" label-width="100px">
        <el-form-item label="secret-id">
          <el-input v-model="txyunConfig.secretId"></el-input>
        </el-form-item>
        <el-form-item label="secret-key">
          <el-input v-model="txyunConfig.secretKey"></el-input>
        </el-form-item>
        <el-form-item label="存储空间名">
          <el-input v-model="txyunConfig.bucketName"></el-input>
        </el-form-item>
        <el-form-item label="地域">
          <el-input v-model="txyunConfig.region"></el-input>
        </el-form-item>
        <el-form-item label="CDN访问域名">
          <el-input v-model="txyunConfig.domain"></el-input>
        </el-form-item>
        <el-button type="primary" size="medium" icon="el-icon-check" :disabled="!isTxyunSave" @click="saveTxyun(true)">
          保存配置
        </el-button>
        <el-button type="info" size="medium" icon="el-icon-close" @click="saveTxyun(false)">清除配置</el-button>
      </el-form>
    </el-card>
    <el-card>
      <div slot="header">
        <span>阿里云存储配置</span>
      </div>
      <el-form :model="aliyunConfig" label-width="100px">
        <el-form-item label="endpoint">
          <el-input v-model="aliyunConfig.endpoint"></el-input>
        </el-form-item>
        <el-form-item label="bucket-name">
          <el-input v-model="aliyunConfig.bucketName"></el-input>
        </el-form-item>
        <el-form-item label="access-key-id">
          <el-input v-model="aliyunConfig.accessKeyId"></el-input>
        </el-form-item>
        <el-form-item label="secret-access-key">
          <el-input v-model="aliyunConfig.secretAccessKey"></el-input>
        </el-form-item>
        <el-button type="primary" size="medium" icon="el-icon-check" :disabled="!isAliyunSave"
                   @click="saveAliyun(true)">保存配置
        </el-button>
        <el-button type="info" size="medium" icon="el-icon-close" @click="saveUpyun(false)">清除配置</el-button>
      </el-form>
    </el-card>
  </div>
</template>

<script>
import {getUserInfo} from "@/api/github";

export default {
  name: "Setting",
  data() {
    return {
      githubToken: '',
      githubUserInfo: {
        login: '未配置'
      },
      isGithubSave: false,
      hintShow: false,
      upyunConfig: {
        username: '',
        password: '',
        bucketName: '',
        domain: ''
      },
      txyunConfig: {
        secretId: '',
        secretKey: '',
        bucketName: '',
        region: '',
        domain: ''
      },
      aliyunConfig: {
        endpoint: '',
        bucketName: '',
        accessKeyId: '',
        secretAccessKey: ''
      }
    }
  },
  computed: {
    isUpyunSave() {
      return this.upyunConfig.username && this.upyunConfig.password && this.upyunConfig.bucketName && this.upyunConfig.domain
    },
    isTxyunSave() {
      return this.txyunConfig.secretId && this.txyunConfig.secretKey && this.txyunConfig.bucketName && this.txyunConfig.region && this.txyunConfig.domain
    },
    isAliyunSave() {
      return this.aliyunConfig.endpoint && this.aliyunConfig.bucketName && this.aliyunConfig.accessKeyId && this.aliyunConfig.secretAccessKey
    }
  },
  created() {
    this.githubToken = localStorage.getItem("githubToken")
    const githubUserInfo = localStorage.getItem('githubUserInfo')
    if (this.githubToken && githubUserInfo) {
      this.githubUserInfo = JSON.parse(githubUserInfo)
      this.isGithubSave = true
    } else {
      this.githubUserInfo = {login: '未配置'}
    }

    const upyunConfig = localStorage.getItem('upyunConfig')
    if (upyunConfig) {
      this.upyunConfig = JSON.parse(upyunConfig)
    }

    const txyunConfig = localStorage.getItem('txyunConfig')
    if (txyunConfig) {
      this.txyunConfig = JSON.parse(txyunConfig)
    }

    const aliyunConfig = localStorage.getItem('aliyunConfig')
    if (aliyunConfig) {
      this.aliyunConfig = JSON.parse(aliyunConfig)
    }


    const userJson = window.localStorage.getItem('user') || '{}'
    const user = JSON.parse(userJson)
    if (userJson !== '{}' && user.role !== 'ROLE_admin') {
      //对于访客模式,增加个提示
      this.hintShow = true
    }
  }
  ,
  methods: {
    // 获取用户信息
    searchGithubUser() {
      getUserInfo(this.githubToken).then(res => {
        this.githubUserInfo = res
        this.isGithubSave = true
      })
    }
    ,
    saveGithub(save) {
      if (save) {
        localStorage.setItem('githubToken', this.githubToken)
        localStorage.setItem('githubUserInfo', JSON.stringify(this.githubUserInfo))
        this.msgSuccess('保存成功')
      } else {
        localStorage.removeItem('githubToken')
        localStorage.removeItem('githubUserInfo')
        this.msgSuccess('清除成功')
      }
    }
    ,
    saveUpyun(save) {
      if (save) {
        localStorage.setItem('upyunToken', btoa(`${this.upyunConfig.username}:${this.upyunConfig.password}`))
        localStorage.setItem('upyunConfig', JSON.stringify(this.upyunConfig))
        this.msgSuccess('保存成功')
      } else {
        localStorage.removeItem('upyunConfig')
        this.msgSuccess('清除成功')
      }
    }
    ,
    saveTxyun(save) {
      if (save) {
        localStorage.setItem('txyunConfig', JSON.stringify(this.txyunConfig))
        this.msgSuccess('保存成功')
      } else {
        localStorage.removeItem('txyunConfig')
        this.msgSuccess('清除成功')
      }
    },
    saveAliyun(save) {
      if (save) {
        localStorage.setItem('aliyunConfig', JSON.stringify(this.aliyunConfig))
        this.msgSuccess('保存成功')
      } else {
        localStorage.removeItem('aliyunConfig')
        this.msgSuccess('清楚成功')
      }
    }
  }
  ,
}
</script>

2.3 index.js新增路由

index.js新增路由

{
	path: 'aliyun',
	name: 'AliyunManage',
	component: () => import('@/views/pictureHosting/AliyunManage.vue'),
	meta: {title: '阿里云', icon: 'el-icon-folder-opened'}
},

image-20240310185937324

2.4 新增AliyunManage.vue

UpyunManage.vue的拿过来复制修改下即可,整体的页面框架差不多,重写里面的增删查方法:

<template>
  <div>
    <el-row>
      <el-select v-model="aliyunConfig.bucketName" disabled style="min-width: 200px"></el-select>
      <el-cascader v-model="activePath" placeholder="请选择目录" :options="pathArr" :props="pathProps" style="min-width: 450px"></el-cascader>
      <el-button type="primary" size="medium" icon="el-icon-search" @click="search">查询</el-button>
      <el-button class="right-item" type="primary" size="medium" icon="el-icon-upload" @click="isDrawerShow=!isDrawerShow">上传</el-button>
    </el-row>
    <el-alert title="只显示<img>标签支持的 apng,avif,bmp,gif,ico,cur,jpg,jpeg,jfif,pjpeg,pjp,png,svg,tif,tiff,webp 格式的图片,见 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/img" type="warning" show-icon close-text="不再提示" v-if="hintShow1" @close="noDisplay(1)"></el-alert>
    <el-alert title="最多显示100个文件" type="warning" show-icon close-text="不再提示" v-if="hintShow2" @close="noDisplay(2)"></el-alert>
    <el-row v-viewer>
      <div class="image-container" v-for="(file,index) in fileList" :key="index">
        <el-image :src="file.url" fit="scale-down"></el-image>
        <div class="image-content">
          <div class="info">
            <span>{{ file.name }}</span>
          </div>
          <div class="icons">
            <el-tooltip class="item" effect="dark" content="复制图片url" placement="bottom">
              <i class="icon el-icon-link" @click="copy(1,file)"></i>
            </el-tooltip>
            <el-tooltip class="item" effect="dark" content="复制MD格式" placement="bottom">
              <SvgIcon icon-class="markdown" class-name="icon" @click="copy(2,file)"></SvgIcon>
            </el-tooltip>
            <i class="icon el-icon-delete" @click="delFile(file)"></i>
          </div>
        </div>
      </div>
    </el-row>

    <el-drawer title="上传文件" :visible.sync="isDrawerShow" direction="rtl" size="40%" :wrapperClosable="false" :close-on-press-escape="false">
      <el-row>
        <el-radio v-model="nameType" label="1">使用源文件名</el-radio>
        <el-radio v-model="nameType" label="2">使用UUID文件名</el-radio>
        <el-button size="small" type="primary" icon="el-icon-upload" v-throttle="[submitUpload,`click`,3000]">确定上传</el-button>
      </el-row>
      <el-row>
        当前目录:{{ realPath }}
      </el-row>
      <el-row>
        <el-switch v-model="isCustomPath" active-text="自定义目录"></el-switch>
        <el-input placeholder="例:oldFolder/newFolder/" v-model="customPath" :disabled="!isCustomPath" size="medium" style="margin-top: 10px"></el-input>
      </el-row>
      <el-upload ref="uploadRef" action="" :http-request="upload" drag multiple :file-list="uploadList" list-type="picture" :auto-upload="false">
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      </el-upload>
    </el-drawer>
  </div>
</template>

<script>
import SvgIcon from "@/components/SvgIcon";
import {isImgExt} from "@/util/validate";
import {randomUUID} from "@/util/uuid";
import {copy} from "@/util/copy";
import OSS from 'ali-oss';

export default {
  name: "AliyunManage",
  components: {SvgIcon},
  data() {
    return {
      ossClient: {},
      aliyunConfig: {
        endpoint: '',
        bucketName: '',
        path: '',
        accessKeyId: '',
        secretAccessKey: ''
      },
      pathArr: [{value: '', label: '根目录'}],
      activePath: [''],//默认选中根目录
      pathProps: {
        lazy: true,
        checkStrictly: true,
        lazyLoad: async (node, resolve) => {
          let path = node.path.join('/')
          let nodes = []
          await this.getReposContents(nodes, path)
          resolve(nodes)
        }
      },
      hintShow1: true,
      hintShow2: true,
      fileList: [],
      isDrawerShow: false,
      nameType: '1',
      uploadList: [],
      isCustomPath: false,
      customPath: '',
    }
  },
  computed: {
    realPath() {
      if (this.isCustomPath) {
        return `/${this.customPath}`
      }
      return `${this.activePath.join('/')}/`
    }
  },
  created() {
    this.hintShow1 = localStorage.getItem('aliyunHintShow1') ? false : true
    this.hintShow2 = localStorage.getItem('aliyunHintShow2') ? false : true

    const aliyunConfig = localStorage.getItem('aliyunConfig')
    if (aliyunConfig) {
      this.aliyunConfig = JSON.parse(aliyunConfig)

      // 初始化OSS客户端。请将以下参数替换为您自己的配置信息。
      this.ossClient = new OSS({
        region: this.aliyunConfig.endpoint.substring(0, this.aliyunConfig.endpoint.indexOf('.')), // 示例:'oss-cn-hangzhou',填写Bucket所在地域。
        accessKeyId: this.aliyunConfig.accessKeyId, // 确保已设置环境变量OSS_ACCESS_KEY_ID。
        accessKeySecret: this.aliyunConfig.secretAccessKey, // 确保已设置环境变量OSS_ACCESS_KEY_SECRET。
        bucket: this.aliyunConfig.bucketName, // 示例:'my-bucket-name',填写存储空间名称。

      });

    } else {
      this.msgError('请先配置阿里云')
      this.$router.push('/pictureHosting/setting')
    }
  },
  methods: {
    //换成懒加载
    async getReposContents(arr, path) {
      await this.ossClient.list({
        prefix: path,
        delimiter: (path ? '' : '/')
      }).then(res => {
        if (res && res.prefixes) {
          res.prefixes.forEach(item => {
            item = item.substring(0, item.indexOf("/"))
            arr.push({value: item, label: item, leaf: false})
          })
        }
      })
    },
    async search() {
      this.fileList = []
      let path = this.activePath.join('/') + '/'
      path = path.startsWith('/') ? path.substring(1) : path
      this.ossClient.list({
        prefix: path,
        delimiter: '/'
      }).then(res => {
        if (res && res.objects) {
          res.objects.forEach(item => {
            if (isImgExt(item.name)) {
              item.path = ''
              this.fileList.push(item)
            }
          })
        }
      })

    },
    noDisplay(id) {
      localStorage.setItem(`aliyunHintShow${id}`, '1')
    },
    copy(type, file) {
      // type 1 cdn link  2 Markdown
      let copyCont = ''
      copyCont = file.url

      copy(copyCont)
      this.msgSuccess('复制成功')
    },
    delFile(file) {
      this.$confirm("此操作将永久删除该文件, 是否删除?", "提示", {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        this.ossClient.delete(file.name).then(() => {
          this.msgSuccess('删除成功')
          this.search()
        })
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除',
        })
      })
    },
    submitUpload() {
      //https://github.com/ElemeFE/element/issues/12080
      this.uploadList = this.$refs.uploadRef.uploadFiles
      if (this.uploadList.length) {
        //触发 el-upload 中 http-request 绑定的函数
        this.$refs.uploadRef.submit()
      } else {
        this.msgError('请先选择文件')
      }
    },
    upload(data) {
      let fileName = data.file.name
      if (this.nameType === '2') {
        fileName = randomUUID() + fileName.substr(fileName.lastIndexOf("."))
      }

      // upload(this.aliyunConfig.bucketName, this.realPath, fileName, data.file).then(() => {
      //   this.msgSuccess('上传成功')
      //   data.onSuccess()
      // })
      if (!this.realPath.endsWith('/')) {
        this.realPath += '/'
      }
      this.ossClient.put(this.realPath + fileName, data.file, {
        'x-oss-storage-class': 'Standard',
        // 指定Object的访问权限。
        'x-oss-object-acl': 'private',
        // 指定PutObject操作时是否覆盖同名目标Object。此处设置为true,表示禁止覆盖同名Object。
        'x-oss-forbid-overwrite': 'true',
      }).then(() => {
        this.msgSuccess('上传成功')
        data.onSuccess()
      })
    },
  },
}
</script>

2.5 测试

部署后查询,成功:

image-20240310190254090

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

http协议分析

目录 一、实验目的 二、实验环境 三、实验步骤 四、实验数据记录和结果分析 五、实验体会、质疑和建议 一、实验目的 通过在真实网络环境访问HTTP服务器上网的过程中,捕获HTTP数据报文,分析报文的内容,掌握HTTP报文的构成,理解HTTP协议的工作过程&#xff0c; 二、实验环境…

02-在 ESP-IDF 项目中添加 .c 和 .h 文件的

在 ESP-IDF 项目中添加 .c 和 .h ESP-IDF&#xff08;Espressif IoT Development Framework&#xff09;是一个用于开发基于 ESP32 和 ESP8266 微控制器的嵌入式应用程序的框架。在 ESP-IDF 项目中添加新的 .c 和 .h 文件是很常见的&#xff0c;但要确保这些文件能够正确地被编…

C++类和对象(下篇)

目录 一.再谈构造函数 二.static成员 三.友元 四.内部类 五. 再次理解类和对象 一.再谈构造函数 1.构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month…

js 获取浏览器相关的宽高尺寸

window 屏幕 屏幕分辨率的高&#xff1a; window.screen.height 屏幕分辨率的宽&#xff1a; window.screen.width 屏幕可用工作区高度&#xff1a; window.screen.availHeight 屏幕可用工作区宽度&#xff1a; window.screen.availWidth document 网页 网页可见区域宽&#xf…

JavaEE+springboot教学仪器设备管理系统o9b00-springmvc

本文旨在设计一款基于Java技术的教学仪器设备销售网站&#xff0c;以提高网站性能、功能完善、用户体验等方面的优势&#xff0c;解决现有教学仪器设备销售网站的问题&#xff0c;并为广大教育工作者和学生提供便捷的教学仪器设备销售渠道。本文首先介绍了Java技术的相关基础知…

三维不同坐标系下点位姿态旋转平移变换

文章目录 前言正文计算方法思路Python实现总结前言 本文主要说明以下几种场景3D变换的应用: 3D相机坐标系下长方体物体,有本身坐标系,沿该物体长边方向移动一段距离,并绕长边轴正旋转方向转90度,求解当前物体中心点在相机坐标系下的位置和姿态多关节机器人末端沿工具坐标…

Redis基础篇:初识Redis(认识NoSQL,单机安装Redis,配置Redis自启动,Redis客户端的基本使用)

目录 1.认识NoSQL2.认识Redis3.安装Redis1.单机安装Redis2.配置redis后台启动3.设置redis开机自启 4.Redis客户端1.Redis命令行客户端2.图形化桌面客户端 1.认识NoSQL NoSQL&#xff08;Not Only SQL&#xff09;数据库是一种非关系型数据库&#xff0c;它不使用传统的关系型数…

我们的一生都是在挤火车。

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 昨天从燕郊坐火车回石家庄&#xff0c;由于赶上元旦假期&#xff0c;所有高铁票都售罄&#xff0c;一张普通火车票&#xff0c;还是一周前就买才买到的。 从燕郊站&#xff0c;到北京站&#xff0c;然后地铁去北京西站…

C语言动态内存管理面(下)常⻅的动态内存的错误

我们接着C语言动态内存管理&#xff08;上&#xff09;没讲完整的继续来深度讲解。、 4. 常⻅的动态内存的错误 4.1 对NULL指针的解引⽤操作 主要的原因还是自己的粗心大意没有对malloc的值进行判断 void test() { int *p (int *)malloc(INT_MAX/4); *p 20; //如果p的值是…

JavaScript原型和原型链

JavaScript每个对象拥有一个原型对象 需要注意的是&#xff0c;只有函数对象才有 prototype 属性 当试图访问一个对象的属性时&#xff0c;它不仅仅在该对象上搜寻&#xff0c;还会搜寻该对象的原型&#xff0c;以及该对象的原型的原型&#xff0c;依次层层向上搜索&#xff…

OpenGrok代码服务器搭建,解决代码检索慢的问题

一、背景 在前一家公司&#xff0c;公司提供了OpenGrok服务器供大家检索查阅代码。但在新公司&#xff0c;大家都使用vscode或Sourse Insight&#xff0c;这就存在一些问题&#xff1a; 不能跳转或者跳转比较慢。 搜索查询速度慢&#xff0c;且结果展示不易查看。 这严重影…

题目:泡澡(蓝桥OJ 3898)

问题描述&#xff1a; 解题思路&#xff1a; 图解&#xff1a;&#xff08;以题目样例为例子&#xff09; 注意点&#xff1a;题目的W是每分钟最大出水量&#xff0c;因此有一分钟的用水量大于出水量则不通过。 补充&#xff1a;差分一般用于对一段区间每个元素加相同值&#x…

nicegui学习使用

https://www.douyin.com/shipin/7283814177230178363 python轻量级高自由度web框架 - NiceGUI (6) - 知乎 python做界面&#xff0c;为什么我会强烈推荐nicegui 秒杀官方实现&#xff0c;python界面库&#xff0c;去掉90%事件代码的nicegui python web GUI框架-NiceGUI 教程…

2024护网面试题精选(二)完

0x02. 内网渗透篇 00- 内网渗透的流程 拿到跳板后&#xff0c;先探测一波内网存活主机&#xff0c;用net user /domian命令查看跳板机是否在域 内&#xff0c;探测存活主机、提权、提取hash、进行横向移动&#xff0c;定位dc位置&#xff0c;查看是否有能直接提权域 管的漏洞…

JavaSE面试——多态

多态 1. 多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。 2. 多态性分为编译时多态和运行时多态&#xff0c;方法重载&#xff08;overload&#xff09;实现的是编译时多态&#xff0c;而方法重写 &…

【运维】本地部署Gemma模型(图文)

工具简介 我们需要使用到两个工具&#xff0c;一个是Ollama&#xff0c;另一个是open-webui。 Ollama Ollama 是一个开源的大语言平台&#xff0c;基于 Transformers 和 PyTorch 架构&#xff0c;基于问答交互方式&#xff0c;提供大语言模型常用的功能&#xff0c;如代码生…

给 spyter/all-spark-notebook 添加scala支持

spyter/all-spark-notebook默认没有安装scala notebook&#xff0c;需要手动添加。 你可以创建一个新的 Dockerfile&#xff0c;在其中添加你需要的配置和组件。以下是一个简单的例子&#xff1a; FROM jupyter/all-spark-notebook:x86_64-ubuntu-22.04 #冒号后可以是latest&a…

CAE模拟仿真工具CEETRON Envision:大数据可视化助力工业设计与协同

行业&#xff1a; 制造业; 工业设计; 汽车&#xff1b;航天 挑战&#xff1a;工业客户需要有效的方法来处理CAE数据&#xff1b;ESTECO寻求提供CAE可视化功能来帮助客户做出决策&#xff1b;许多可用的可视化工具无法提供对模型中数据的完全访问以进行深入分析 解决方案&…

快速排序hoare优化

目录 三数取中法选key 优化点 基本思想 代码实现 小区间优化 优化点 基本思想 代码实现 由于hoare版快排在一些特殊情况下性能并不优&#xff0c;这里我们进行一些优化。 三数取中法选key 优化点 当数据有序时&#xff0c;快排就会很吃力&#xff0c;这是为什么呢…

OpenJDK 目前主要发展方向

Loom&#xff1a;得赶紧解决 synchronized pin 线程的问题&#xff08;据说 Java 23 会解决&#xff0c;现在有预览版&#xff09;。各个 Java 库需要改造原来使用 ThreadLocal 的方式&#xff1a;如果是为了穿参数&#xff0c;则可以使用 ScopedLocal&#xff1b;如果是对象池…