uniapp - 小程序实现摄像头拍照 + 水印绘制 + 反转摄像头 + 拍之前显示时间+地点 + 图片上传到阿里云服务器

news2025/1/4 11:44:15

前言

uniapp,碰到新需求,反转摄像头,需要在打卡的时候对上传图片加上水印,拍照前就显示当前时间日期+地点,拍摄后在呈现刚才拍摄的图加上水印,最好还需要将图片上传到阿里云。

声明

水印部分代码是借鉴的这位博主的博客,剩下的是我根据自己的需求加上的。水印部分看原博主博客就行。
小晗同学 - 原小程序拍照+水印绘制博主博客链接跳转

效果预览

拍摄前预览
右上角切换前后摄像头
底部时间和位置信息,这里位置替换掉真实位置了,代码里没变
在这里插入图片描述
拍摄后效果
在这里插入图片描述

水印组件代码

<template>
    <view class="camera-wrapper">
        <!-- 拍照 -->
        <template v-if="!snapSrc">
            <!-- 相机 -->
            <camera :device-position="cameraPosition" flash="off" @error="handleError" class="image-size">
                <view class="photo-btn" @click="handleTakePhoto">拍照</view>
                <view class="iconfont icon-qiehuanshexiangtou switch-camera-btn" @click="handleSwitchCamera"></view>
                <view class="time-wrap">
                    <view>{{ new Date().toLocaleString() }}</view>
                    <view>{{ location_data }}</view>
                    <!-- <view>这里是位置信息,</view> -->
                </view>
            </camera>
            <!-- 水印 -->
            <canvas canvas-id="photoMarkCanvas" id="photoMarkCanvas" class="mark-canvas"
                :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }" />
        </template>
        <!-- 预览 -->
        <template v-else>
            <!-- <view class="re-photo-btn btn" @click="handleRephotograph">重拍</view> -->
            <!-- <view class="re-fanhui-btn btn" @click="fanhui">返回</view> -->
            <image class="image-size" :src="snapSrc"></image>
        </template>
    </view>
</template>
<script>
const util_two = require('../../static/utils/util_two.js')
const upload = require('../../static/utils/upload.js')
export default {
    name: 'CameraSnap',
    props: {
        // 照片地址(若传递了照片地址,则默认为预览该照片或添加水印后预览)
        photoSrc: {
            type: String,
            default: ""
        },
        // 水印类型
        markType: {
            type: String,
            default: "fixed", // 定点水印 fixed,背景水印 background
        },
        // 水印文本列表(支持多行)
        markList: {
            type: Array,
            default: () => []
        },
        textColor: {
            type: String,
            default: "#FFFFFF"
        },
        textSize: {
            type: Number,
            default: 32
        },
        // 定点水印的遮罩(为了让水印更清楚)
        useTextMask: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            snapSrc: "",
            canvasWidth: "",
            canvasHeight: "",
            cameraPosition: 'back', // 默认为后置摄像头
            inputText: "", // 用户输入的文本
            location: null, // 存储位置信息
            location_data: "",

            photocount: 10,
            hasUserInfo: false,
            productInfo: [],
            fileurl: [],
            prveImgInfo: [],
            imgs: [],
            arrImg: [],
            imgQueRemData: [],//确实上传数据源
            // 位置和时间日期
        }
    },
    watch: {
        photoSrc: {
            handler: function (newValue, oldValue) {
                if (newValue) {
                    this.getWaterMarkImgPath(newValue)
                }
            },
            immediate: true
        }
    },
    mounted() {
        uni.getLocation({
            type: 'wgs84', // 获取经纬度坐标
            success: (res) => {
                this.location = res;
                setTimeout(() => {
                    this.GetMapData();
                }, 1000);
            },
            fail: (err) => {
                console.error('获取位置信息失败', err);
            }
        });
    },
    methods: {

        closeCamera() {
            this.$emit('close'); // 发送一个事件通知父组件关闭 CameraSnap 组件
        },
        handleTakePhoto() {
            // const that = this
            const ctx = uni.createCameraContext();
            ctx.takePhoto({
                quality: 'high',
                success: (res) => {
                    const imgPath = res.tempImagePath
                    if (this.markList.length) {
                        this.getWaterMarkImgPath(imgPath)
                        this.$emit('watermarkPath', this.snapSrc);

                    } else {
                        this.snapSrc = imgPath;
                        this.$emit('complete', imgPath)
                        this.$emit('watermarkPath', this.snapSrc);
                    }
                }
            });
        },
        handleRephotograph() {
            this.snapSrc = ""
        },
        handleSwitchCamera() {
            this.cameraPosition = this.cameraPosition === 'front' ? 'back' : 'front'; // 切换摄像头
        },
        handleError(err) {
            uni.showModal({
                title: '警告',
                content: '若不授权使用摄像头,将无法使用拍照功能!',
                cancelText: '不授权',
                confirmText: '授权',
                success: (res) => {
                    if (res.confirm) {
                        // 允许打开授权页面,调起客户端小程序设置界面,返回用户设置的操作结果
                        uni.openSetting({
                            success: (res) => {
                                res.authSetting = { "scope.camera": true }
                            },
                        })
                    } else if (res.cancel) {
                        // 拒绝打开授权页面
                        uni.showToast({ title: '您已拒绝授权,无法进行拍照', icon: 'error', duration: 2500 });
                    }
                }
            })
        },
        setWaterMark(context, image) {
            const listLength = this.markList?.length
            switch (this.markType) {
                case 'fixed':
                    const spacing = 6 // 行间距
                    const paddingTopBottom = 60 // 整体上下间距
                    // 默认每行的高度 = 字体高度 + 向下间隔
                    const lineHeight = this.textSize + spacing
                    const allLineHeight = lineHeight * listLength
                    // 矩形遮罩的 Y 坐标
                    const maskRectY = image.height - allLineHeight
                    // 绘制遮罩层
                    if (this.useTextMask) {
                        context.setFillStyle('rgba(0,0,0,0.4)');
                        context.fillRect(0, maskRectY - paddingTopBottom, image.width, allLineHeight + paddingTopBottom)
                    }
                    // 文本与 x 轴之间的间隔
                    const textX = 40
                    // 文本一行的最大宽度(减去 20 是为了一行的左右留间隙)
                    const maxWidth = image.width - 20
                    context.setFillStyle(this.textColor)
                    context.setFontSize(this.textSize)
                    this.markList.forEach((item, index) => {
                        // 因为文本的 Y 坐标是指文本基线的 Y 轴坐标,所以要获取文本顶部的 Y 坐标
                        const textY = maskRectY - paddingTopBottom / 2 + this.textSize + lineHeight * index
                        context.fillText(item, textX, textY, maxWidth);
                    })
                    break;
                case 'background':
                    context.translate(0, 0);
                    context.rotate(30 * Math.PI / 180);
                    context.setFillStyle(this.textColor)
                    context.setFontSize(this.textSize)
                    const colSize = parseInt(image.height / 6)
                    const rowSize = parseInt(image.width / 2)
                    let x = -rowSize
                    let y = -colSize
                    // 循环绘制 5 行 6 列 的文字
                    for (let i = 1; i <= 6; i++) {
                        for (let j = 1; j <= 5; j++) {
                            context.fillText(this.markList[0], x, y, rowSize)
                            // 每个水印间隔 20
                            x += rowSize + 20
                        }
                        y += colSize
                        x = -rowSize
                    }
                    break;
            }
            context.save();
        },
        getWaterMarkImgPath(src) {
            const _this = this
            uni.getImageInfo({
                src,
                success: (image) => {
                    this.canvasWidth = image.width
                    this.canvasHeight = image.height
                    const context = uni.createCanvasContext("photoMarkCanvas", this)
                    context.drawImage(src, 0, 0, image.width, image.height)
                    // 设置水印
                    this.setWaterMark(context, image)
                    // 若还需其他操作,可在操作之后叠加保存:context.restore()
                    // 将画布上的图保存为图片
                    context.draw(false, () => {
                        setTimeout(() => {
                            uni.canvasToTempFilePath({
                                destWidth: image.width,
                                destHeight: image.height,
                                canvasId: 'photoMarkCanvas',
                                fileType: 'jpg',
                                success: function (res) {
                                    console.log("将画布上的图保存为图片", JSON.parse(JSON.stringify(res)));
                                    _this.snapSrc = res.tempFilePath
                                    const tempFilePath = res.tempFilePath;
                                    const tempFilePathArray = [tempFilePath];
                                    _this.uploadimg({
                                        path: tempFilePathArray //这里是选取的图片的地址数组
                                    });
                                    _this.$emit('complete', _this.snapSrc)
                                }
                            },
                                _this
                            );
                        }, 200)
                    });
                }
            })
        },
        fanhui() {
            this.closeCamera();
        },
        sendUploadedImagesToParent() {
            this.$emit('imagesUploaded', this.prveImgInfo);
            // prveImgInfo  imgs
        },
        //多张图片上传  服务器
        uploadimg: function (data) {
            // 这两个是对应的,传递的就是这个路径
            var that = this;
            // var orderid = that.XmID;//项目id
            var orderid = '';
            // var gsid = '';
            let photocount = 9;
            var i = data.i ? data.i : 0; //当前上传的哪张图片
            var success = data.success ? data.success : 0; //上传成功的个数
            var fail = data.fail ? data.fail : 0; //上传失败的个数
            //上传到阿里云
            util_two.request(uni.$baseUrlweb + '/api/xcx/oss/fankui').then(function (result) {
                if (result.code == 0) {
                    // var filePath = data.path;
                    var filePath = data.path[i];
                    var filename = result.dir + orderid + upload.calculate_object_name(filePath, '');
                    uni.uploadFile({
                        url: result.host,
                        filePath: filePath,
                        name: "file",
                        /**上传的参数**/
                        formData: {
                            'key': filename, // 文件名
                            'policy': result.policy,
                            'OSSAccessKeyId': result.accessid,
                            'success_action_status': "200",
                            'signature': result.signature,
                            'callback': result.callback
                        },
                        success: (resp) => {
                            if (resp.statusCode == "200") {
                                success++; //图片上传成功,图片上传成功的变量+1
                                photocount--;
                                var show_url = result.host + '/' + filename + result.style1;
                                var productInfo = that.productInfo;
                                productInfo.push(show_url);
                                var prve_url = result.host + '/' + filename + result.style2;
                                var prveImgInfo = that.prveImgInfo;
                                prveImgInfo.push(prve_url);
                                that.sendUploadedImagesToParent();
                                var up_url = filename;
                                var fileurl = that.fileurl;
                                fileurl.push(up_url);
                                let n = i + 1;
                                uni.showLoading({
                                    title: n + '/' + data.path.length + '上传成功', //这里打印出 上传成功
                                })
                            }
                        },
                        fail: (res) => {
                            fail++; //图片上传失败,图片上传失败的变量+1
                            uni.showLoading({
                                title: (i + 1) + '/' + data.path.length + '上传失败', //这里打印出 上传成功
                            })
                        },
                        complete: (res) => {
                            i++; //这个图片执行完上传后,开始上传下一张
                            if (i == data.path.length) { //当图片传完时,停止调用     
                                // 添加上传数据
                                that.imgQueRemData.push(filename)
                                // 添加到展示数组
                                const path = result.host + '/' + filename + result.style1
                                that.arrImg.push(path)
                                that.imgs.push(path)

                                uni.hideLoading();
                                that.closeCamera();
                                if (success == i) {
                                    uni.showToast({
                                        title: '组图上传完成', //这里打印出 上传成功
                                        icon: 'success',
                                        duration: 1000
                                    })
                                } else {
                                    uni.showModal({
                                        title: '组图上传失败', //这里打印出 上传成功
                                        content: '请稍后再试',
                                        showCancel: false
                                    })
                                }
                            } else { //若图片还没有传完,则继续调用函数
                                data.i = i;
                                data.success = success;
                                data.fail = fail;
                                that.uploadimg(data);
                            }
                        }
                    });
                }
            })
        },
        // 获取具体位置信息
        async GetMapData() {
            const res = await this.$axios("work/getMap", {
                lat: this.location.latitude,
                lon: this.location.longitude
            });
            if (res.data.code == 0) {
                this.location_data = res.data.result;
            } else {
                uni.showToast({
                    title: res.data.msg,
                    icon: 'none',
                    duration: 1000
                })
            }
        },
    }
}
</script>
<style lang="scss" scoped>
.icon-qiehuanshexiangtou {
    font-size: 30px;
}

.camera-wrapper {
    position: relative;
}

.switch-camera-btn {
    position: absolute;
    top: 20px;
    right: 20px;
    color: #fff;
    font-size: 16px;
    cursor: pointer;
}

.mark-canvas {
    position: absolute;
    /* 将画布移出展示区域 */
    top: -200vh;
    left: -200vw;
}

.image-size {
    width: 100%;
    height: 100vh;
}

.photo-btn {
    position: absolute;
    bottom: 120rpx;
    left: 50%;
    transform: translateX(-50%);
    width: 140rpx;
    height: 140rpx;
    line-height: 140rpx;
    text-align: center;
    background-color: #000000;
    border-radius: 50%;
    border: 10rpx solid #ffffff;
    color: #fff;
}

.btn {
    padding: 10rpx 20rpx;
    background-color: #000000;
    border-radius: 10%;
    border: 6rpx solid #ffffff;
    color: #fff
}

.re-photo-btn {
    position: absolute;
    bottom: 150rpx;
    right: 40rpx;

}

.re-fanhui-btn {
    position: absolute;
    bottom: 150rpx;
    right: 180rpx;


}

.re-fanhui-tijao {
    position: absolute;
    bottom: 150rpx;
    right: 320rpx;

}

.time-wrap {
    position: absolute;
    left: 1rem;
    bottom: 1rem;
    color: white;
}
</style>

使用水印相机组件代码

<template>
	<view>
	    <view class="qianDao_img">
			<view class="imgs">
				<view style="margin-right: 10px;">
					照片:
				</view>
				<view @click="paizhao" class="paizhao">
					<view class="iconfont icon-paizhao1"></view>
				</view>
			</view>
			<view class="img_wrap">
				<image v-for="(item, index) in shuiyinImg" :key="index" :src="item" mode="scaleToFill"
					@click="SYpreviewImage(index)" @longpress="SYdeleteImage(index)" />
			</view>
		</view>

		<!-- <button @click="paizhao">拍照</button> -->
		<view class="full-screen" v-if="paizhaoType">
			<CameraSnap @imagesUploaded="handleImagesUploaded" @close="paizhaoType = false"
				:mark-list="[new Date().toLocaleString(), location_data]" textSize="24" useTextMask />
		</view>
		<view class="Bom_Btn">
			<view @click="BaoCunAction" class="Bom_Btn_log">
				<view>提交</view>
			</view>
		</view>
	</view>
</template>

<script>


import CameraSnap from '../CameraSnap.vue'

export default {
	data() {
		return {
			paizhaoType: false,
			location: null, // 存储位置信息
			location_data: "",
			// 水印图片
			shuiyinImg: '',
		};
	},
	components: {
		CameraSnap,
	},
	onLoad(options) {
		uni.getLocation({
			type: 'wgs84', // 获取经纬度坐标
			success: (res) => {
				this.location = res;
				setTimeout(() => {
					this.GetMapData();
				}, 1000);
			},
			fail: (err) => {
				console.error('获取位置信息失败', err);
			}
		});
	},

	methods: {
	    // 这里是水印图片
		handleImagesUploaded(images) {
			console.log("成功上传的图片数据:", images);
			this.shuiyinImg = [...this.shuiyinImg, ...images];
		},
		paizhao() {
			this.paizhaoType = true;
		},
		// 获取具体位置信息
		async GetMapData() {
			const res = await this.$axios("work/getMap", {
				lat: this.location.latitude,
				lon: this.location.longitude
			});
			if (res.data.code == 0) {
				this.location_data = res.data.result;
			} else {
				uni.showToast({
					title: res.data.msg,
					icon: 'none',
					duration: 1000
				})
			}
		},
		// 水印图片预览
		SYpreviewImage(index) {
			uni.previewImage({
				urls: this.shuiyinImg,
				current: index, // 当前显示图片的索引
				loop: true // 是否开启图片轮播
			});
		},
		// 长按删除水印图片
		SYdeleteImage(index) {
			uni.showModal({
				title: '提示',
				content: '确定要删除这张图片吗?',
				success: (res) => {
					if (res.confirm) {
						this.shuiyinImg.splice(index, 1);
					}
				}
			});
		},
	
	
		
		
	}
}
</script>

<style lang="scss" scoped>
.full-screen {
	position: fixed;
	top: 0;
	left: 0;
	width: 100vw;
	height: 100vh;
	// background-color: rgba(0, 0, 0, 0.5); /* 半透明背景 */
	z-index: 9999;
	/* 确保在最顶层显示 */
}

// 签到图片
.qianDao_img {
	// padding: 15px;
	border-top: 1px solid #dddddf;
	background-color: white;

	// 图片
	.imgs {
		padding: 15px;
		display: flex;
		align-items: center;
		flex-wrap: wrap;

	}

	.img_wrap {
		padding: 10px;
		display: flex;
		// justify-content: space-around;
		flex-wrap: wrap;

		image {
			width: 80px;
			height: 80px;
			margin-bottom: 5px;
			margin: 4px;
		}
	}

	// 上传图片
	.paizhao {
		width: 80px;
		height: 80px;
		// border: 1px solid red;
		margin: 10px 0;
		background-color: #f7f8fa;

		.icon-paizhao1 {
			// border: 1px solid red;
			width: 50%;
			font-size: 22px;
			padding-left: 25px;
			padding-top: 25px;
		}
	}
}

.Bom_Btn {
	width: 100%;
	padding: 10px;
	position: absolute;
	bottom: 0;
	left: 0;
	border-top: 1px solid #ededed;
	background-color: white;
	z-index: 2;

	.Bom_Btn_log {
		width: 70%;
		// display: flex;
		// align-items: center;
		// justify-content: center;
		// margin: auto;
		padding: 10px 0;
		margin: auto;
	}

	view {
		border-radius: 5px;
		text-align: center;
		color: white;
		background-color: #1989fa;
	}
}
</style>

只能说勉强够用,算不上精细,凑合看把,引入的两个js文件 都是辅助上传阿里云图片的,就是格式啥的进行校验,就没必要上传了,看懂整个思路就行,具体代码肯定还是需要根据自己的情况进行改动的

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

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

相关文章

2024年12月31日Github流行趋势

项目名称&#xff1a;free-programming-books 项目地址url&#xff1a;https://github.com/EbookFoundation/free-programming-books项目语言&#xff1a;HTML历史star数&#xff1a;344575今日star数&#xff1a;432项目维护者&#xff1a;vhf, eshellman, davorpa, MHM5000, …

基于SpringBoot+Vue实现停车场管理系统

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

Java学习路线:Servlet(一)认识和创建Servlet

目录 创建Servlet WebServlet Servlet的生命周期 认识和使用HttpServlet Servlet是JavaEE的一个标准&#xff0c;他就像JDBC一样&#xff0c;由官方定义了一系列接口&#xff0c;而具体的实现由我们自己编写&#xff0c;最后交给Web服务器如Tomcat来运行我们编写的Servlet…

公路边坡安全监测中智能化+定制化+全面守护的应用方案

面对公路边坡的安全挑战&#xff0c;我们如何精准施策&#xff0c;有效应对风险&#xff1f;特别是在强降雨等极端天气下&#xff0c;如何防范滑坡、崩塌、路面塌陷等灾害&#xff0c;确保行车安全&#xff1f;国信华源公路边坡安全监测解决方案&#xff0c;以智能化、定制化为…

机器人对物体重定向操作的发展简述

物体重定向操作的发展简述 前言1、手内重定向和外部重定向2、重定向原语3、重定向状态转换网络4、连续任意姿态的重定向5、利用其他环境约束重定向总结Reference 前言 对于一些特殊的任务&#xff08;如装配和打包&#xff09;&#xff0c;对物体放置的位姿由明确的要求&#…

【AndroidAPP】权限被拒绝:[android.permission.READ_EXTERNAL_STORAGE],USB设备访问权限系统报错

一、问题原因 1.安卓安全性变更 Android 12 的安全性变更&#xff0c;Google 引入了更严格的 PendingIntent 安全管理&#xff0c;强制要求开发者明确指定 PendingIntent 的可变性&#xff08;Mutable&#xff09;或不可变性&#xff08;Immutable&#xff09;。 但是&#xf…

windows系统安装完Anaconda之后怎么激活自己的虚拟环境并打开jupyter

1.在win主菜单中找到Anaconda安装文件夹并打开终端 文件夹内有所有安装后的Anaconda的应用软件和终端窗口启动窗口 点击Anaconda Prompt&#xff08;Anaconda&#xff09;就会打开类似cmd的命令终端窗口&#xff0c;默认打开的路径是用户名下的路径 2.激活虚拟环境 使用命令…

django33全栈班2025年004 录入数据

前言 通过前面的学习, 我们已经算是Python基本入门了. 如果你能熟练的掌握的话, 至少让你换台电脑, 在新电脑上搭建Python的开发环境肯定是没问题的. 我们呢也学习了第一行Python代码, 但是我们不知道这行代码是什么意思, 为什么能够运行, 怎么就能输出到控制台呢? 还有, …

Zeotero安装”translate for Zotero“插件

一、Zeotero6translate for Zotero 1.0.28 二、打开Zeotero官网&#xff0c;找到下面圈起来的 三、点击以上连接跳转&#xff0c;Releases windingwind/zotero-pdf-translate 下载 zotero-pdf-翻译.xpi 四、打开zeotero&#xff0c;工具>附加组件&#xff08;或插件&am…

郑州时空-TMS运输管理系统 GetDataBase 信息泄露漏洞复现

0x01 产品简介 郑州时空-TMS运输管理系统是一款专为物流运输企业设计的综合性管理软件,旨在提高运输效率、降低运输成本,并实现供应链的协同运作。系统基于现代计算机技术和物流管理方法,结合了郑州时空公司的专业经验和技术优势,为物流运输企业提供了一套高效、智能的运输…

小程序信息收集(小迪网络安全笔记~

免责声明&#xff1a;本文章仅用于交流学习&#xff0c;因文章内容而产生的任何违法&未授权行为&#xff0c;与文章作者无关&#xff01;&#xff01;&#xff01; 附&#xff1a;完整笔记目录~ ps&#xff1a;本人小白&#xff0c;笔记均在个人理解基础上整理&#xff0c;…

前端(九)js介绍(2)

js介绍(2) 文章目录 js介绍(2)一、函数1.1函数的两种形式1.2函数的作用域1.3声明与提升 二、bom操作三、dom操作 一、函数 1.1函数的两种形式 //有参函数 //js中的函数只能返回一个值&#xff0c;如果要返回多个需要放在数组或对象中 function func(a,b){return ab } func(1,…

国标GB28181-2022视频平台EasyGBS如何获取设备镜像ID?

在安防监控领域&#xff0c;随着技术的发展和标准的统一&#xff0c;国标GB28181-2022成为了视频监控系统互联互通的重要协议。EasyGBS作为一个遵循该国标的平台&#xff0c;为用户提供了强大的视频监控和管理功能。 在EasyGBS平台的使用过程中&#xff0c;设备镜像ID的获取是一…

【ADAS】高级驾驶辅助系统

自动驾驶入门—ADAS&#xff08;Advanced Driving Assistance System&#xff09;高级辅助驾驶系统 一、ADAS的官方介绍 二、信息辅助 1、行车监控类 2、危险预警类 3、驾驶便利类 三、控制辅助 1、紧急应对类 2、驾驶便利类 3、是车道保持类 4、智能灯光类 参考链接&#xff1…

Spring Boot + MinIO 实现分段、断点续传,让文件传输更高效

一、引言 在当今的互联网应用中&#xff0c;文件上传是一个常见的功能需求。然而&#xff0c;传统的文件上传方式在面对大文件或不稳定的网络环境时&#xff0c;可能会出现性能瓶颈和上传失败的问题。 传统文件上传&#xff0c;就像是用一辆小推车搬运大型家具&#xff0c;一…

搭建android开发环境 android studio

1、环境介绍 在进行安卓开发时&#xff0c;需要掌握java&#xff0c;需要安卓SDK&#xff0c;需要一款编辑器&#xff0c;还需要软件的测试环境&#xff08;真机或虚拟机&#xff09;。 早起开发安卓app&#xff0c;使用的是eclipse加安卓SDK&#xff0c;需要自行搭建。 目前开…

开发过程优化·自定义鼠标右键菜单

为了改善日常工作中的开发体验&#xff0c;我希望在某个项目目录下点击鼠标右键的快捷菜单&#xff0c;让程序自动为该项目引入一个内部的工具库文件并挂载到项目中。 实现该功能需要组装一些零碎的电脑应用知识&#xff0c;下面徐徐渐进依次说明&#xff1a; 1、在右键菜单中…

搭建ZooKeeper分布式集群

ZooKeeper分布式集群部署旨在通过多节点协作实现高可用性和容错能力。本次实战以三台服务器&#xff08;master、slave1、slave2&#xff09;为例&#xff0c;详细介绍了从下载安装到配置启动的全过程。首先&#xff0c;下载并解压ZooKeeper安装包至/usr/local目录&#xff0c;…

elasticsearch-java客户端jar包中各模块的应用梳理

最近使用elasticsearch-java客户端实现对elasticsearch服务的Api请求&#xff0c;现对elasticsearch-java客户端jar包中各模块的应用做个梳理。主要是对co.elastic.clients.elasticsearch路径下的各子包的简单说明。使用的版本为&#xff1a;co.elastic.clients:elasticsearch-…

【AIGC】使用Java实现Azure语音服务批量转录功能:完整指南

文章目录 引言技术背景环境准备详细实现1. 基础架构设计2. 实现文件上传功能3. 提交转录任务crul4. 获取转录结果 使用示例结果示例最佳实践与注意事项总结 引言 在当今数字化时代&#xff0c;将音频内容转换为文本的需求越来越普遍。无论是会议记录、视频字幕生成&#xff0c…