【Hadoop/Java】基于HDFS的Java Web网络云盘

news2024/11/17 4:37:56

【Hadoop/Java】基于HDFS的Java Web网络云盘

本人BNUZ大学生萌新,水平不足,还请各位多多指教!

实验目的

  1. 熟悉HDFS Java API的使用;
  2. 能使用HDFS Java API编写应用程序

实验要求

云盘系统通过互联网为企业和个人提供信息的储存、读取、下载等服务,具有安全稳定、海量存储的特点。根据用户群定位,云盘系统可以划分为公有云盘、社区云盘、私有云盘等。请利用HDFS Java API,编写一个云盘系统,要求实现功能如下:
云盘系统基本功能图

环境

Ubuntu 20.04LTS + Java (OpenJDK 8) + IDEA Ultimate 2021.3.3 + Hadoop 3.3.2

项目下载

Github仓库:https://github.com/gennwolf/yunpan-hadoop

使用框架以及Web服务器

Maven + SpringBoot 2.6.4 + SpringMVC + Apache Tomcat 9.0.58
前端模板来源:https://colorlib.com (使用了Bootstrap + jQuery)

分布式集群配置

有3个节点,每个节点的主机名、IP以及担任的角色如下表所示:

节点主机名IP角色
1Master192.168.170.111NameNode, SecondaryNameNode, DataNode, ResourceManager, NodeManager
2Slave1192.168.170.112DataNode, NodeManager
3Slave2192.168.170.113DataNode, NodeManager
网关子网掩码
192.168.170.2255.255.255.0

实验步骤

  1. 启动Hadoop集群,分别到各个节点使用jps命令查看进程是否与上表匹配:
    启动集群
    下图为Master节点的Java进程,和上表对应节点的角色相匹配:
    查看Master节点的进程
    可以在Master上用ssh连接到其他节点查看进程

  2. 在HDFS的根目录下创建一个文件userinfo.dat用于保存用户信息,用户信息包含用户名和密码,用逗号分隔,该文件用于云盘的登录/注册等操作,我们往里面添加两个用户spring和summer,密码都为123456,如下图:
    查看userinfo.dat文件内容

  3. 每个用户只能访问自己的资源,用以前上数据库系统原理课程的知识来说,就是每一名用户都有自己的文件目录视图,在本次实验中,我的想法是,每一名用户对应一个目录,比如说用户spring对应HDFS中的目录/spring,用户summer对应/summer,用户spring是不能操作用户summer的文件目录的,这样就实现了用户之间的隔离性,确保每位用户访问到的是自己的资源,我们在/spring下创建一个文本文件test.txt,再创建一个目录dd,在dd下也创建一个文本文件test.txt,创建文件可以用touch命令,创建目录可以用mkdir命令,我们递归打印/spring目录下的所有文件查看是否创建成功,到这里就完成了文件的准备工作:
    递归打印/spring目录所有内容

  4. 打开IDEA,创建SpringBoot项目,配置好Web相关依赖后,加入Hadoop依赖,依赖的version对应当前系统中的Hadoop版本:

	<dependency>
	    <groupId>org.apache.hadoop</groupId>
	    <artifactId>hadoop-client</artifactId>
	    <version>3.3.2</version>
	</dependency>
  1. 项目的结构如图:
    项目结构
    关键的文件及目录说明如下表所示:
名称类型目录
Controller目录用于存放Controller类
FileSystemControllerJava类用于接收前端的请求,从而进行请求处理以及HDFS操作的Controller类
PageControllerJava类用于接收前端请求,实现网页跳转的Controller类
domain目录用于存放数据类
HDFSFileJava类用于描述HDFS文件信息的数据类,含有三个字段,name表示名称,date表示修改日期,type表示类型(文件/目录)
YunpanHadoopApplicationJava类用于启动Springboot项目的类
resources.static目录用于存放静态资源的目录,本项目的前端的所有静态资源,包括CSS、JS、图片等,都放在此目录下
application.properties配置文件SpringBoot配置文件
webapp.WEB-INF目录存放JSP页面
pom.xml配置文件Maven配置文件

下面围绕页面来进行功能讲解,本项目页面数量很少,只有3个页面:

  1. 由于操作HDFS,需要频繁创建FileSystem实例,所以我把创建FileSystem实例写成了一个函数,方便以后调用:
    //实例化Configuration和FileSystem
    public FileSystem getFileSystem() throws IOException {
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS", "hdfs://192.168.170.111:9000");
        return FileSystem.get(conf);
    }
  1. 另外本web项目全程session绑定,用户登陆后就设置名为LOGIN_STATUS的session,用户退出登录则删除该session。同时也设置一个名为path的session,用于记录当前用户访问的HDFS目录地址
    //当前登录状态检查
    public boolean loginStatusCheck(HttpSession session) {
        return session.getAttribute("LOGIN_STATUS") != null;
    }
  1. index.jsp页面以及register.jsp页面
    index.jsp页面全貌:
    index.jsp
    点击“没有账号?点击此处注册 →”按钮,跳转到注册页面register.jsp,跳转功能用PageController类实现,对应的跳转语句:
    前端(前端语句只示例一次):
	<div class="text-center p-t-136">
		<a class="txt2" href="<%=request.getContextPath()%>/jumpToRegisterPage">
		没有账号?点击此处注册
		<i class="fa fa-long-arrow-right m-l-5" aria-hidden="true"></i>
		</a>
	</div>

后台:

    @RequestMapping("/jumpToRegisterPage")
    public String jumpToRegisterPage() {
        return "register";
    }

跳转到register.jsp页面,页面全貌:
register.jsp
输入用户名,密码以及确认密码,每一项都不能为空,前端做了约束:
约束
两次密码必须输入一致,后台做了约束,否则注册不成功:
约束
执行注册操作,用户名为test,密码为test,后台获取前台的数据,注册操作相关代码:

    //注册
    @RequestMapping("/register")
    public String register(HttpServletRequest request, Model model) throws IOException {
        String username = request.getParameter("username");
        String userpasswd = request.getParameter("userpasswd");
        String userpasswd_confirm = request.getParameter("userpasswd_confirm");
        if(!userpasswd.equals(userpasswd_confirm)) {
            model.addAttribute("status", "两次密码输入不一致!");
            return "register";
        }
        if(userExistCheck(username)) {
            model.addAttribute("status", "用户已存在!");
            return "register";
        }
        insertUserInfoToFile(username, userpasswd);
        mkdir("/" + username);
        model.addAttribute("status", "注册成功,请登录!");
        return "index";
    }

注册的时候要检查该用户是否已经存在,调用userExistCheck方法来检查:

    //判断用户是否已经注册
    public boolean userExistCheck(String username) throws IOException {
        FileSystem fs = getFileSystem();
        Path srcPath = new Path("/userinfo.dat");
        FSDataInputStream in = fs.open(srcPath);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = "";
        while((line = reader.readLine()) != null) {
            String[] userinfo = line.split(",");
            if(userinfo[0].equals(username)) {
                fs.close();
                return true;
            }
        }
        fs.close();
        return false;
    }

若用户之前没有注册过,则允许该用户注册,注册时把用户信息写入到userinfo.dat中,调用insertUserInfoToFile方法:

    //把新注册的用户信息插入到userinfo.dat中
    public void insertUserInfoToFile(String username, String userpasswd) throws IOException {
        FileSystem fs = getFileSystem();
        Path srcPath = new Path("/userinfo.dat");
        FSDataOutputStream out = fs.append(srcPath);
        String userinfo = username + "," + userpasswd + "\n";
        out.write(userinfo.getBytes(StandardCharsets.UTF_8));
        out.close();
        fs.close();
    }

同时在HDFS中创建该用户的个人目录/test,调用mkdir方法:

    //创建目录核心操作
    public void mkdir(String path) throws IOException {
        FileSystem fs = getFileSystem();
        Path srcPath = new Path(path);
        fs.mkdirs(srcPath);
        fs.close();
    }

注册成功后,跳转到登录页面,并且给出提示要求登录:
提示
观察userinfo.dat文件,发现添加了新注册的用户信息:
查看userinfo.dat
观察HDFS根目录,发现添加了test用户的个人目录:
查看根目录
下面我们用spring用户登录,输入用户名spring,密码123456,点击登录按钮,把相关表单数据提交到Controller的login方法处理:
登录

    //登录
    @RequestMapping("/login")
    public String login(HttpServletRequest request, HttpSession session, Model model) throws IOException {
        String username = request.getParameter("username");
        String userpasswd = request.getParameter("userpasswd");
        if(!loginCheck(username, userpasswd)) {
            model.addAttribute("status", "用户名或密码错误!");
            return "index";
        }
        session.setAttribute("LOGIN_STATUS", username);
        session.setAttribute("path", "/" + username);
        model.addAttribute("currentpath", "/" + username);
        model.addAttribute("filelist", getFileList("/" + username));
        return "myfiles";
    }

登录时后台会检查用户名和密码是否正确,调用loginCheck方法:

    //检查用户名和密码是否正确
    public boolean loginCheck(String username, String userpasswd) throws IOException {
        FileSystem fs = getFileSystem();
        Path srcPath = new Path("/userinfo.dat");
        FSDataInputStream in = fs.open(srcPath);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = "";
        while((line = reader.readLine()) != null) {
            String[] userinfo = line.split(",");
            if(userinfo[0].equals(username) && userinfo[1].equals(userpasswd)) {
                fs.close();
                return true;
            }
        }
        fs.close();
        return false;
    }

若登录不成功,则刷新登陆页面,并给出文字提示:
提示
若登陆成功,则设置session,LOGIN_STATUS的值为当前用户的用户名,path设置为用户的个人目录,然后跳转到个人网盘页面myfiles.jsp
9. myfiles.jsp页面:
个人网盘页面,页面全貌:
myfiles.jsp
该页面有上传文件按钮,表单左上角显示当前所在路径,表单第一行提供返回上级目录功能,然后就是文件列表,若为目录,则在类型列显示目录,若为文件,则在类型列显示文件,另外显示文件或目录的创建时间,最右边一列提供删除文件或目录的功能,左下角提供创建目录/退出登录以及注销账户的功能
当登录成功后,跳转到个人网盘页面,在页面打印用户个人目录下的文件,我们用的是spring用户,所以我们打印HDFS中/spring路径下的所有文件和目录,以及相关的信息,由于文件创建时间在HDFS中用的是时间戳表示,所以我们要先转换为我们方便看的时间,格式为年-月-日 时:分:秒,每一个文件或者目录相关信息我都放在HDFSFile数据类里面:

import lombok.Data;

@Data
public class HDFSFile {
    private String name;
    private String date;
    private String type;
}

通过遍历得到文件和目录列表,用List类型封装HDFSFile数据,然后我们要保证目录始终在文件列表的前面,所以我排了个序,得到最终的文件列表,该方法名为getFileList方法:

    //获取特定路径的所有文件
    public List<HDFSFile> getFileList(String path) throws IOException {
        FileSystem fs = getFileSystem();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<HDFSFile> fileList = new ArrayList<>();
        FileStatus[] fileStatuses = fs.listStatus(new Path(path));
        for(FileStatus fileStatus: fileStatuses) {
            HDFSFile file = new HDFSFile();
            file.setName(fileStatus.getPath().getName());
            file.setDate(format.format(fileStatus.getModificationTime()));
            if(fileStatus.isDirectory())
                file.setType("目录");
            else
                file.setType("文件");
            fileList.add(file);
        }
        //排序,目录放前面,文件放后面
        Collator collator = Collator.getInstance(Locale.CHINA);
        fileList.sort((f1, f2) -> (collator.compare(f1.getType(), f2.getType())));
        return fileList;
    }

当点击文件列表中的目录的时候,改变session,把path的值变成当前目录,然后刷新页面,显示你选的目录中的文件,若点击文件列表中的文件,则下载该文件,处理文件和目录请求用Controller的fileHandle方法:

    //处理文件和目录请求,如果是目录,则跳转到对应目录中去,如果是文件,则下载文件
    @RequestMapping("/fileHandle")
    public String fileHandle(HttpSession session, HttpServletRequest request, Model model, HttpServletResponse response) throws IOException {
        if(!loginStatusCheck(session)) {
            model.addAttribute("status", "此操作需要你登录!");
            return "index";
        }
        String filename = request.getParameter("filename");
        String filetype = request.getParameter("type");
        if(filetype.equals("目录")) {
            session.setAttribute("path", session.getAttribute("path").toString() + "/" + filename);
            model.addAttribute("currentpath", session.getAttribute("path").toString());
            model.addAttribute("filelist", getFileList(session.getAttribute("path").toString()));
            return "myfiles";
        }
        FileSystem fs = getFileSystem();
        FSDataInputStream in = fs.open(new Path(session.getAttribute("path").toString() + "/" + filename));
        response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
        BufferedInputStream bufferedInputStream = new BufferedInputStream(in);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(response.getOutputStream());
        byte[] buff = new byte[2048];
        int bytesRead;
        while((bytesRead = bufferedInputStream.read(buff, 0, buff.length)) != -1)
            bufferedOutputStream.write(buff, 0, bytesRead);
        bufferedInputStream.close();
        bufferedOutputStream.close();
        fs.close();
        return null;
    }

若为下载,需要触发浏览器事件,所以要改变header,让浏览器执行下载操作:
文件下载
若为目录跳转,则用新路径重新运行getFileList方法,刷新页面:
点击目录
目录跳转
因为涉及HDFS核心操作,所以若检测到LOGIN_STATUS session不存在,则跳转到登陆页面要求用户登录,后面的核心操作也同样设定了安全约束:
安全约束
点击返回上层目录,则更新session,改变path,去掉path的最后一个/右边内容以及最后一个/,然后重新调用getFileList方法,刷新页面,请求交给Controller的back方法处理:
返回上层目录

    //返回上一级目录
    @RequestMapping("/back")
    public String back(HttpSession session, HttpServletRequest request, Model model) throws IOException {
        if(!loginStatusCheck(session)) {
            model.addAttribute("status", "此操作需要你登录!");
            return "index";
        }
        String currentpath = session.getAttribute("path").toString();
        String[] pathsplit = currentpath.split("/");
        if(pathsplit.length == 2) {
            model.addAttribute("warning", "alert(\"当前已经是根目录!\");");
            model.addAttribute("currentpath", session.getAttribute("path").toString());
            model.addAttribute("filelist", getFileList(session.getAttribute("path").toString()));
            return "myfiles";
        }
        StringBuilder sb = new StringBuilder();
        for(int i = 1; i < pathsplit.length - 1; i++)
            sb.append("/").append(pathsplit[i]);
        session.setAttribute("path", sb.toString());
        model.addAttribute("currentpath", session.getAttribute("path").toString());
        model.addAttribute("filelist", getFileList(session.getAttribute("path").toString()));
        return "myfiles";
    }

若当前已经是用户个人目录的最上层,若还点返回上层目录,则弹出警告,并刷新页面:
提示
点击test.txt最右边的删除按钮,则删除文件,若点击的是目录的删除,则会顺带删除该目录下面的所有文件,删除文件或目录的请求交给Controller的deleteFile方法处理,然后核心操作交给delete方法处理:
删除文件

    //删除文件或目录
    @RequestMapping("/deleteFile")
    public String deleteFile(HttpSession session, HttpServletRequest request, Model model) throws IOException {
        if(!loginStatusCheck(session)) {
            model.addAttribute("status", "此操作需要你登录!");
            return "index";
        }
        String filename = request.getParameter("filename");
        delete(session.getAttribute("path").toString() + "/" + filename);
        model.addAttribute("warning", "alert(\"删除成功!\");");
        model.addAttribute("currentpath", session.getAttribute("path").toString());
        model.addAttribute("filelist", getFileList(session.getAttribute("path").toString()));
        return "myfiles";
    }
    //删除文件或目录核心操作
    public void delete(String path) throws IOException {
        FileSystem fs = getFileSystem();
        Path srcPath = new Path(path);
        fs.delete(srcPath, true);
        fs.close();
    }

删除成功后弹出提示框,然后刷新页面:
提示
删除文件后刷新页面
点击创建目录按钮,则弹出提示框:
创建目录
弹窗
输入新目录的名称,输入不能为空,否则会弹出警示:
提示
若输入目录正确,则创建目录,刷新页面,创建目录请求交给Controller的makeDirectory方法处理:

    //创建目录
    @RequestMapping("/makeDirectory")
    public String makeDirectory(HttpSession session, HttpServletRequest request, Model model) throws IOException {
        String dirname = request.getParameter("dirname");
        if(fileExist(session.getAttribute("path").toString() + "/" + dirname))
            model.addAttribute("warning", "alert(\"该目录已存在,请重新输入目录名!\");");
        else {
            mkdir(session.getAttribute("path").toString() + "/" + dirname);
            model.addAttribute("warning", "alert(\"创建成功!\");");
        }
        model.addAttribute("currentpath", session.getAttribute("path").toString());
        model.addAttribute("filelist", getFileList(session.getAttribute("path").toString()));
        return "myfiles";
    }

输入目录名
创建成功后弹出提示:
提示
页面刷新后可以看到刚刚创建的目录:
刷新页面后的页面
若要创建的目录已经存在,则弹出警告,刷新页面:
提示
检测目录或文件是否存在,用fileExist方法处理:

    //判断文件或目录是否存在
    public boolean fileExist(String path) throws IOException {
        FileSystem fs = getFileSystem();
        boolean isExist = fs.exists(new Path(path));
        fs.close();
        return isExist;
    }

选择文件,然后点击上传,若没有选择文件直接点击上传,前台做了相关约束,不允许提交请求:
提示
点击选择文件按钮,弹出文件选择框,选中文件,点击打开,则网页显示文件的名称:
我的文件
文件信息
点击上传,由于上传需要时间,在上传过程中,不可进行浏览器的刷新等其他操作,很可能会造成文件上传失败,所以在等待上传的时间里,页面会显示警示文字:
文件上传提示
文件上传成功后,弹出提示框提示用户:
提示
然后页面刷新,文件已经在文件列表中:
页面刷新后
用命令行查看/spring目录,文件已经上传到HDFS中:
查看/spring目录
若上传的文件已经在HDFS中了,如果重复上传,则不允许执行上传操作,并弹出警告,刷新页面:
提示
上传文件操作请求交给Controller的upload方法处理,这里用了commons-io以及commons-fileupload插件,需要在Maven POM文件中导入相关依赖:

	<dependency>
		<groupId>commons-fileupload</groupId>
		<artifactId>commons-fileupload</artifactId>
		<version>1.4</version>
	</dependency>
	<dependency>
		<groupId>commons-io</groupId>
		<artifactId>commons-io</artifactId>
		<version>2.11.0</version>
	</dependency>
    //文件上传
    @RequestMapping("/upload")
    public String upload(HttpSession session, HttpServletRequest request, Model model) throws FileUploadException, IOException {
        FileSystem fs = getFileSystem();
        boolean isExist = false;
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setHeaderEncoding("UTF-8");
        List<FileItem> list = upload.parseRequest(request);
        for(FileItem item: list) {
            if(!item.isFormField()) {
                String filename = item.getName();
                if(filename == null || filename.trim().equals(""))
                    continue;
                isExist = fileExist(session.getAttribute("path").toString() + "/" + filename);
                if(isExist)
                    continue;
                fs = getFileSystem();
                InputStream in = item.getInputStream();
                FSDataOutputStream out = fs.create(new Path(session.getAttribute("path").toString() + "/" + filename));
                byte[] buff = new  byte[2048];
                int bytesRead = 0;
                while((bytesRead = in.read(buff)) > 0)
                    out.write(buff, 0, bytesRead);
                in.close();
                out.close();
                item.delete();
            }
        }
        model.addAttribute("currentpath", session.getAttribute("path").toString());
        model.addAttribute("filelist", getFileList(session.getAttribute("path").toString()));
        if(isExist)
            model.addAttribute("warning", "alert(\"网盘有同名文件,建议更改文件名!\");");
        else
            model.addAttribute("warning", "alert(\"文件上传成功!\");");
        fs.close();
        return "myfiles";
    }

判断文件是否存在使用上面提到的fileExist方法处理
点击退出登录按钮,则清空LOGIN_STATUS session,然后返回登陆页面,要求客户登录:
退出登录
对应的退出登录请求交给Controller的logout方法处理:

    //退出登录
    @RequestMapping("/logout")
    public String logout(HttpSession session) {
        session.removeAttribute("path");
        session.removeAttribute("LOGIN_STATUS");
        return "index";
    }

用户点击注销账户按钮,则弹出选择框让用户再次确认是否注销账户:
注销账户
提示框
当用户点击确认后,则开始执行注销用户任务,注销成功后,弹出提示框提示用户:
提示
注销用户请求交给Controller的deleteUser方法处理:

    //注销账户
    @RequestMapping("/deleteUser")
    public String deleteUser(HttpSession session, Model model) throws IOException {
        String username = session.getAttribute("path").toString().split("/")[1];
        delete("/" + username);
        removeUserFromFile(username);
        model.addAttribute("warning", "alert(\"注销成功!\");");
        session.removeAttribute("path");
        session.removeAttribute("LOGIN_STATUS");
        return "index";
    }

首先调用上面提到的delete方法,删除该用户的个人目录/spring,然后修改userinfo.dat,删除该用户的账号密码信息,修改userinfo.dat使用removeUserFromFile方法:

    //从userinfo.dat中移除用户
    public void removeUserFromFile(String username) throws IOException {
        FileSystem fs = getFileSystem();
        Path srcPath = new Path("/userinfo.dat");
        FSDataInputStream in = fs.open(srcPath);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        StringBuilder sb = new StringBuilder();
        String line = "";
        while((line = reader.readLine()) != null) {
            String[] userinfo = line.split(",");
            if(userinfo[0].equals(username))
                continue;
            sb.append(line).append("\n");
        }
        in.close();
        FSDataOutputStream out = fs.create(srcPath, true);
        out.write(sb.toString().getBytes(StandardCharsets.UTF_8));
        out.close();
        fs.close();
    }

查看HDFS根目录,发现spring用户的个人目录已经被移除:
查看根目录
查看userinfo.dat文件,发现spring用户的个人信息已经被删除:
查看userinfo.dat
然后清空session,返回登陆页面,完成注销账户的所有操作

至此本云盘的所有功能以及代码都说明完毕!

存在的一些问题

后端的话,我觉得优化空间还是很大的,代码的逻辑上应该是可以优化的

关于前端的话,我属于前端小白来着,非常不会写前端
我觉得文件上传可以写一个进度条,然后一些交互通讯其实可以用AJAX来处理

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

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

相关文章

前后端部署+nginx配置

文章目录概要1、脚手架安装2、项目打包部署3、配置nginxEND概要 内容主要包括部署前端项目&#xff0c;nginx安装配置&#xff0c;以及后端项目的打包 1、脚手架安装 vue init webpack 项目运行&#xff08;默认端口8080&#xff09; npm run dev 如果前后端分离项目&…

使用Vue+el-form+form-validate实现管理端登录接口联调前准备工作实战

前言 这是《Vue + SpringBoot前后端分离项目实战》专栏的第7篇博客,感谢你能从成千上万篇博客中打开这一篇,和我一起学习前端开发实战知识,让我们一起开始吧。 目录 前言 一、上节回顾和本节介绍 1. 上节回顾

【可视化开发】echarts配置项——修改tooltip默认样式

修改tooltip默认样式 在可视化开发中我们通常会遇到修改tooltip样式问题&#xff0c;下面分享给大家代码片段和最终呈现效果 tooltip: {//鼠标悬浮框的提示文字trigger: "axis",axisPointer: {// 坐标轴指示器配置项。type: "none", // line 直线指示器 …

Vue3.0ElementPlus<input输入框自动获取焦点>

文章目录前言一、input-focus事件&#xff1f;二、使用步骤1.给input 设置ref 属性2.引入ref和nextTick3.在dialog打开事件中触发前言 记录一下自己最近开发vue3.0的小小问题~~ 最近在做项目时&#xff0c;dialog弹框事件需定位input焦点&#xff0c;方便用户可直接输入。原理…

【vue3】组合式API之setup()介绍与reactive()函数的使用

>&#x1f609;博主&#xff1a;初映CY的前说(前端领域) ,&#x1f4d2;本文核心&#xff1a;setup()概念、 reactive()的使用 【前言】vue3作为vue2的升级版&#xff0c;有着很多的新特性&#xff0c;其中就包括了组合式API&#xff0c;也就是是 Composition API。学习组合…

vue + gojs 实现拖拽 流程图

一、流程图效果 最近一段时间在研究go.js,它是一款前端开发画流程图的一个插件&#xff0c;也是一个难点&#xff0c;要说为什么是难点&#xff0c;首先&#xff0c;它是依赖画布canvas知识开发。其次&#xff0c;要依赖于内部API开发需求&#xff0c;开发项目需求的时候就要花…

js逆向爬取某音乐网站某歌手的歌曲

js逆向爬取某音乐网站某歌手的歌曲一、分析网站1、案例介绍2、寻找列表页Ajax入口&#xff08;1&#xff09;页面展示图。&#xff08;2&#xff09;寻找部分歌曲信息Ajax的token。&#xff08;3&#xff09;寻找歌曲链接&#xff08;4&#xff09;获取歌曲名称和id信息3、寻找…

vue-plugin-hiprint vue hiprint vue使用hiprint打印控件VUE HiPrint HiPrint简单使用

vue-plugin-hiprint vue hiprint vue使用hiprint打印控件VUE HiPrint HiPrint简单使用安装相关依赖安装 vue-plugin-hiprintJQuery安装 打印客户端引入依赖打印 html 内容 - 简单使用根据模版打印 - 简单使用以下内容 和上面demo 没关系 &#xff01;&#xff01;&#xff01;&…

使用videjs+vue2+elementui自定义播放器控件

一、安装项目所需依赖 videojs依赖&#xff1a; npm install --save-dev video.js elementui依赖&#xff08;这个图方便就不按需引入了&#xff09;&#xff1a; npm i element-ui -S 二、main.js修改 增加以下几行&#xff1a; import videojs from video.js import e…

成功解决:下载的谷歌浏览器,打开却是“2345浏览器”,方法亲测有效

今天打开谷歌浏览器使用&#xff0c;浏览器界面显示的2345浏览器&#xff0c;难道谷歌把2345收购了&#xff1f;应该不能&#xff0c;上网查找问题原因才发现&#xff0c;原来的谷歌首页是被劫持了。&#xff08;如果迫切解决问题&#xff0c;直接拉到底找方法&#xff09; 试了…

前端必备的谷歌浏览器JSON可视化插件:JSON-Handle

功能简介: 日常开发过程中,对接后台返回的数据接口时,数据格式常常是各种json格式字符串,在netWork里面查看十分不便,需要在网上找一个json格式化的工具再查看,然后再取数据字段,然后绑定到页面上,十分不便,推荐这么一款前端开发的浏览器插件工具给大家使用。 返回数…

【React-Hooks进阶】useState回调函数的参数 / useEffect发送网络请求/ useRef / useContext

前言 博主主页&#x1f449;&#x1f3fb;蜡笔雏田学代码 专栏链接&#x1f449;&#x1f3fb;React专栏 上篇文章初步学习了Hooks的基础知识 今天来深入学习Hooks的一些扩展知识 感兴趣的小伙伴一起来看看吧~&#x1f91e; 文章目录useState -回调函数的参数使用场景语法语法规…

el-switch

目录 在element ui中el-switch开关组件具有先改变开关值再传值的特点。&#xff08;先改后传&#xff09; 1、触发change事件时 2、绑定disabled属性写三元表达式时 3、解决办法 在element ui中el-switch开关组件具有先改变开关值再传值的特点。&#xff08;先改后传&#xf…

cron表达式 详解

corn表达式是&#xff1a;由若干数字、空格、符号按一定的规则&#xff0c;组成的一组字符串&#xff0c;从而表达时间的信息。 好像和正则表达式有点类似哈&#xff0c;都是一个字符串表示一些信息。Cron 表达式生成器&#xff1a; https://www.smart-tools.cn/cron简介Cron 表…

基于Java+MySQL 实现(Web)日程管理系统【100010222】

基于Java的日程管理系统开发 摘要 日程管理在日常生活中是十分普通的一件事情&#xff0c;人们无论在生活中还是工作中都会有大大小小、各种各样的事情安排&#xff0c;如果仅仅靠纸张或者自己记录这些事情&#xff0c;往往会遗忘。针对这样的痛点&#xff0c;本文提供了日程…

HTML小图标的使用(无需下载图标源码)

我们在浏览一个网页中&#xff0c;会遇到很多有趣的小图标&#xff0c;这些小图标与访问者形成了友好的互动&#xff0c;所以我们在开发中都会适当插入一些生动有趣的图标来吸引访问者。插入图标的网站有很多&#xff0c;我这里以阿里巴巴图标库&#xff08;iconfont-阿里巴巴矢…

Div标签里放img和span标签实现垂直水平居中

正常默认布局 代码实现&#xff1a; <div style"width: 400px; height:400px; background-color:blueviolet"><img style"width: 80px; height: 80px;" src"./picture.png"><span style"color:white;">我是span标…

Vue3 使用MD5加密(清晰明了)

概述 最近在想做个cloud项目,gitee上找了个模板项目&#xff0c;前端使用到vue3 typeScript、Element Plus、Vue Router、Pinia、Axios、i18n、Vite等技术&#xff0c;最近使用到vue3 MD5加密&#xff0c;顺便学习一下&#xff0c;在此总结一下&#xff0c;若有不足之处&…

《EDA前端软件开发工程师面试指南》

2023届EDA领域校招总结&#xff0c;完结撒花&#xff01;&#xff01;&#xff01; 目录 前言 一、EDA公司介绍 二、项目面试 1.自我介绍 2.项目深入 3.专业经验 4.成果和技能 5.对面试官有什么问题 三、C面试 1、高频考点 2、其他知识点 3、算法题 四、逻辑综合面…

【前端代码实例】使用HTML5+CSS3+JavaScript制作一个响应式的后台管理系统~带侧边导航栏仪表盘功能

bilibili在线视频演示地址: 【前端代码实例】使用HTML5+CSS3+JavaScript制作一个响应式的后台管理系统~带侧边导航栏仪表盘功能 效果图: 完整代码: <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta…