Layui_HDFS目录(上传,下载,删除,分页,下级目录,键盘控制返回上一页)

注:转载请署名

一、实体

package com.ebd.application.modules.fileManage.pojo;

public class FilesOrDirs {

	private String id;
	private String permission;
	private String owner;
	private String group;
	private String type;
	private String size;
	private String modificationTime;
	private String name;
	private String dirs;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPermission() {
		return permission;
	}
	public void setPermission(String permission) {
		this.permission = permission;
	}
	public String getOwner() {
		return owner;
	}
	public void setOwner(String owner) {
		this.owner = owner;
	}
	public String getGroup() {
		return group;
	}
	public void setGroup(String group) {
		this.group = group;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public String getSize() {
		return size;
	}
	public void setSize(String size) {
		this.size = size;
	}
	public String getModificationTime() {
		return modificationTime;
	}
	public void setModificationTime(String modificationTime) {
		this.modificationTime = modificationTime;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getDirs() {
		return dirs;
	}
	public void setDirs(String dirs) {
		this.dirs = dirs;
	}
}

二、前端页面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Access-Control-Allow-Origin" content="*">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<%
	String path = request.getContextPath();
	String rootPath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ "/";
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path;
	request.setAttribute("basePath", basePath);
	request.setAttribute("rootPath", rootPath);
	pageContext.setAttribute("newLineChar", "\n");
%>
<link rel="stylesheet" href="${basePath}/static/layui/css/layui.css" media="all" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_tnyc012u2rlwstt9.css" media="all" />
<link rel="stylesheet" href="${basePath}/static/css/files.css" media="all" />
<style type="text/css">
	
</style>
</head>
<body class="childrenBody">
	<blockquote class="layui-elem-quote dir_search">
		<input id="checkids" type="hidden">
		<div class="layui-inline">
		    <div class="layui-input-inline">
		    	<input type="text" value="/" placeholder="请输入HFDS目录" style="width:800px" disabled="disabled" class="layui-input search_input">
		    </div>
			<!-- <a class="layui-btn layui-btn-lg search_btn" id="search">查询</a>  #该方式被放弃-->
			<p class="layui-form-mid layui-word-aux">提示:ctrl(cmd)+backspace 返回上一级</p>
		</div>
	</blockquote>
	<div class="layui-form dirs_list">
	  	<table class="layui-table">
		    <colgroup>
				<col width="50">
				<col>
				<col width="10%">
				<col width="10%">
				<col width="10%">
				<col width="10%">
				<col width="15%">
				<col width="15%">
				<col width="18%">
		    </colgroup>
		    <thead>
				<tr>
					<th><input type="checkbox" lay-skin="primary" lay-filter="allChoose" id="allChoose"></th>
					<th>权限</th>
					<th>所属</th>
					<th>组别</th>
					<th>类型</th>
					<th>大小</th>
					<th>上次修改</th>
					<th>名称</th>
					<th>操作</th>
				</tr> 
		    </thead>
		    <tbody class="files_content" ></tbody>
		</table>
	</div>
	<div id="page"></div>
	<script type="text/javascript" src="${basePath}/static/js/jquery.js"></script>
	<script type="text/javascript" src="${basePath}/static/layui/layui.js"></script>
	<script type="text/javascript">
        $(function(){
              //初始化列表及分页
              submit();
              //点击查询时触发(该功能使用绑定回车标签实现-也就是刷新)
//               $("#search").click(function(){ 
//             	//当点击搜索的时候,起始搜索值为0-10,且当前页回到第一页
//                	startPage = 0; 
//                	currentPage = 1;
//             	submit();
//               })
        });

        //分页参数设置 这些全局变量关系到分页的功能
        var startPage = 0;
        var limitNum = 10;
        var currentPage = 1;
        var totalPage = 0;
        //ajax请求后台数据
        function submit(){
        	var path = $(".search_input").val()
            $.ajax({
                type:"post",
                async:false,
                url:"${basePath}/file/fileListByPage.htm",
                data:{start:startPage, limit:limitNum,path:path},
                success:function(data,status){
                    data=eval("("+data+")"); //转为对象
                    startPage = data.currentIndex;//当前页数(后台返回)
                    totalPage = data.totalPage;//总页数(后台返回)
                    toPage(data.dataList);
                }
            });
        }

        //看看参数有的是否为空(data:后台数据)
        function toPage(data){
            layui.use(['form', 'layer', 'layedit', 'laydate', 'laypage'], function() {
            	var form = layui.form(),
                layer = layui.layer,
                layedit = layui.layedit,
                laydate = layui.laydate,
                laypage = layui.laypage;
                //调用分页
                laypage({
                    cont: 'page'
                    ,pages: totalPage 
                    ,curr: currentPage
                    ,skip: true
                    ,jump: function(obj, first){
                        currentPage = obj.curr;
                        startPage = (obj.curr-1)*limitNum;
                        getBackDatas(data); //渲染数据
                        if(!first){ //加此判断,避免初始时会无限刷新
                      	  submit(); //一定要把翻页的ajax请求放到这里,不然会请求两次。
                        }
                        form.render(); 
                    }
                  });
                //全选
                form.on('checkbox(allChoose)', function (data) {
        	        var checkvals="";
                	var child = $(data.elem).parents('table').find('tbody input[type="checkbox"]:not([name="show"])');
                	child.each(function (index, item) {
        	            item.checked = data.elem.checked;
        	            if(item.checked){
        	            	checkvals += ","+item.value;
        	            }
        	        });
        	        //捕捉被选记录个数及id
        	        $("#checkids").attr("valength",checkvals.split(",").length-1);
        	        $("#checkids").val(checkvals);
        	        form.render('checkbox');
        	    });
               //通过判断文章是否全部选中来确定全选按钮是否选中
               form.on("checkbox(choose)", function (data) {
            	    var checkvals=""; 
            	    var child = $(data.elem).parents('table').find('tbody input[type="checkbox"]:not([name="show"])');
                    var childChecked = $(data.elem).parents('table').find('tbody input[type="checkbox"]:not([name="show"]):checked')
                    if (childChecked.length == child.length) {
                        $(data.elem).parents('table').find('thead input#allChoose').get(0).checked = true;
                    } else {
                        $(data.elem).parents('table').find('thead input#allChoose').get(0).checked = false;
                    }
                    //遍历被选中的记录
                    for(var i=0;i<childChecked.length;i++){
                    	checkvals += ","+childChecked[i].value;
                    }
                  	//捕捉被选记录个数及id
                    $("#checkids").attr("valength",childChecked.length);
                    $("#checkids").val(checkvals);
                    form.render('checkbox');
                })
            });
        };
        
        //遍历装载数据
        function getBackDatas(data){
        	var htmlStr = "";
        	$.each(data,function(v,o){
        		htmlStr+="<tr><td><input type=\"checkbox\" lay-skin=\"primary\" class='cd_checkbox' value=\""+o.id+"\" lay-filter=\"choose\"></td>";
        		htmlStr+='<td>'+o.permission+'</td>';
        		htmlStr+='<td>'+o.owner+'</td>';
        		htmlStr+='<td>'+o.group+'</td>';
        		htmlStr+='<td>';
        		if(o.type == 'd'){
        			htmlStr+='目录';
        		}else{
        			htmlStr+='文件';
        		}
        		htmlStr+='</td>';
        		htmlStr+='<td>'+o.size+'</td>';
        		htmlStr+='<td>'+o.modificationTime+'</td>';
        		htmlStr+='<td>'+o.name+'</td>';
        		htmlStr+='<td>';
        		if(o.type == 'f'){
        			htmlStr+='<a class=\"layui-btn layui-btn-mini data_down\" οnclick=\"data_down(\''+o.dirs+'\')\"><i class=\"layui-icon\"></i>下载</a>';
        		}else{
        			htmlStr+='<a class=\"layui-btn layui-btn-mini data_up\" οnclick=\"data_up(\''+o.dirs+'\')\"><i class=\"layui-icon\"></i>上传</a>';
        			htmlStr+='<a class=\"layui-btn layui-btn-mini data_next\" οnclick=\"data_next(\''+o.dirs+'\')\"><i class=\"layui-icon\"></i>进入</a>';
        		}
        		htmlStr+='<a class=\"layui-btn layui-btn-danger layui-btn-mini data_del\" οnclick=\"data_del(\''+o.dirs+'\')\" data-id=\"'+o.id+'\"><i class=\"layui-icon\"></i>删除</a>';
        		htmlStr+='</td></tr>';
        	});
            if(data.length>0){
            	$("#page").show();
                $(".files_content").html(htmlStr);
            }else{
                $("#page").hide();
                $(".files_content").html("<tr><td colspan='9'>暂无数据</td></tr>");
            }
            
          	//清空checkids(checkbox记录)
	        $("#checkids").attr("valength",0);
	        $("#checkids").val("");
        }
        
        //前一个目录(暂时没用)
        function data_prev(dirs,pathName){
       	  $(".search_input").val(dirs.replace(pathName,''));
     	  submit();
        }
        //后一个目录
        function data_next(dirs){
       	  $(".search_input").val(dirs);
     	  submit();
        }
        function data_up(remoteDir){
            layer.prompt({
        		value: '举栗 D:/wx/install.txt',
        		title: '请输入本地文件路径',
        		maxlength: 100
       		}, function(value, index){
            	var dirRegex = /^([A-Za-z]{1}:\/[\w\/]*)?\w+\.{1}[a-zA-Z]+$/;
            	if(!dirRegex.exec(value)){
            		layui.layer.alert('友情提示:请输入正确的目录</br>注意:间隔符为 / 且目录不能有特殊字符', {
    				  skin: 'layui-layer-molv' 
    				  ,closeBtn: 0
    				});
            	}else{
            		$.ajax({
    					type : "POST",
    					async : false,
    					dataType : "JSON",
    					cache : false,
    					url : "${basePath}/file/fileup.htm",
    					data : {localFile:value, remoteDir:remoteDir},
    					success : function(data) {
    						if (data.flag) {
    							layer.msg(data.msg, {
      							  icon:data.msgIcon,
      							  time:data.msgTime 
      							}); 
    						} else {
    							layer.msg(data.msg, {
    							  icon:data.msgIcon,
    							  time:data.msgTime 
    							});  
    						}
    					}
    				});
            	}
       		  layer.close(index);
       		});
        }
        function data_down(remoteFile){
            layer.prompt({
        		value: '举栗 D:/download',
        		title: '请输入本地目录',
        		maxlength: 100
       		}, function(value, index){
            	var dirRegex = /^([A-Za-z]{1}:\/[\w\/]*)?\w+[/|\w]$/;
            	if(!dirRegex.exec(value)){
            		layui.layer.alert('友情提示:请输入正确的目录</br>注意:间隔符为 / 且目录不能有特殊字符', {
    				  skin: 'layui-layer-molv' 
    				  ,closeBtn: 0
    				});
            	}else{
            		$.ajax({
    					type : "POST",
    					async : false,
    					dataType : "JSON",
    					cache : false,
    					url : "${basePath}/file/filedown.htm",
    					data : {remoteFile:remoteFile, targetDir:value},
    					success : function(data) {
    						if (data.flag) {
    							layer.msg(data.msg, {
      							  icon:data.msgIcon,
      							  time:data.msgTime 
      							}); 
    						} else {
    							layer.msg(data.msg, {
    							  icon:data.msgIcon,
    							  time:data.msgTime 
    							});  
    						}
    					}
    				});
            	}
       		  layer.close(index);
       		});
        }
        function data_del(remoteFileOrDir){
        	layer.confirm('友情提示:你确定要删除该文件或文件夹吗', {skin: 'layui-layer-molv' }, function(index){
        		$.ajax({
    				type : "POST",
    				async : false,
    				dataType : "JSON",
    				cache : false,
    				url : "${basePath}/file/fileordirdel.htm",
    				data : {remoteFileOrDir:remoteFileOrDir},
    				success : function(data) {
    					if (data.flag) {
    						layer.msg(data.msg, {
    						  icon:data.msgIcon,
    						  time:data.msgTime 
    						}, function(){
    							submit();
    						});  
    					} else {
    						layer.msg(data.msg, {
    						  icon:data.msgIcon,
    						});  
    					}
    				}
    			});
        		layer.close(index);
        	}); 
        }
        
        //绑定搜索框回车事件(暂未使用)
//         $('.search_input').bind('keypress', function (event) { 
// 		   if(event.keyCode == "13") { 
// 			  submit();
// 		   }
// 		})
		
		//监听按键事件
		document.onkeydown = function(e) {
	        var keyCode = e.keyCode || e.which || e.charCode;
	        var ctrlKey = e.ctrlKey || e.metaKey;
	        if(keyCode == 8) {
	        	var sval = $('.search_input').val();
	        	if(sval!=''&&sval.substring(0,1)=='/'&&sval!='/'){
	        		var prevdir;
	        		if(sval.substring(sval.length-1)!='/'){
	        			prevdir = sval.substring(0,sval.lastIndexOf("/"));
	        			$(".search_input").val(prevdir);
	        		}
	        		if(prevdir==''){
	        			$('.search_input').val('/');
	        		}
	        		//重置开始页面数
	        		startPage=0;
	        		submit();
	        	}
	        }
	       //e.preventDefault();
	       return true;
	    }
    </script>
</body>
</html>

三、控制器

package com.ebd.application.modules.fileManage.controller;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.ebd.application.common.Base.Page;
import com.ebd.application.common.support.RetObj;
import com.ebd.application.modules.fileManage.pojo.FilesOrDirs;
import com.ebd.application.modules.fileManage.service.FileService;

@Controller
@Scope("prototype") 
@RequestMapping("/file")
public class FileController {
	
	// 日志记录器
	public final Logger log = Logger.getLogger(this.getClass());
	
	@Autowired
	private FileService fileService;
	
	@RequestMapping("filemanage")
	public String fileManage(HttpServletRequest request, String currPage) {
		
		return "modules/file/filemanage";
	}
	
	@ResponseBody
	@RequestMapping("fileListByPage")
	public Page<FilesOrDirs> listByPage(Integer start, Integer limit, String path) {
		
		Map<String,Object> conditions = new HashMap<String,Object>();
		conditions.put("path", StringUtils.isNotBlank(path) ? path : "/");
		Page<FilesOrDirs> page = new Page<FilesOrDirs>(start,limit,conditions);
        return fileService.findByPage(page);
	}
	
	@ResponseBody
	@RequestMapping("fileup")
	public RetObj fileUp(String localFile, String remoteDir) {
		return fileService.fileUp(localFile,remoteDir);
	}
	
	
	@ResponseBody
	@RequestMapping("filedown")
	public RetObj fileDown(String remoteFile, String targetDir) {
        return fileService.fileDown(remoteFile,targetDir);
	}
	
	@ResponseBody
	@RequestMapping("fileordirdel")
	public RetObj fileOrDirDel(String remoteFileOrDir) {
		return fileService.fileOrDirDel(remoteFileOrDir);
	}
	
}

四、FileDao类

package com.ebd.application.modules.fileManage.dao;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.mapred.JobConf;

import com.ebd.application.common.Base.Constants;
import com.ebd.application.common.Base.Page;
import com.ebd.application.common.support.RetObj;
import com.ebd.application.common.utils.CreateFileUtil;
import com.ebd.application.common.utils.DateUtils;
import com.ebd.application.common.utils.Identities;
import com.ebd.application.modules.fileManage.pojo.FilesOrDirs;

public class FileDao {
	
	//hdfs路径
	private static String hdfsPath;
	
	//Hadoop系统配置
	private static Configuration conf;
	
	//fdfs对象
	private static FileDao hdfsObj;
	
	private static List<FilesOrDirs> filesList = null;
	
	public FileDao(Configuration conf) {
		this(Constants.HDFS, conf);
	}
	
	public FileDao(String hdfs, Configuration conf) {
		this.hdfsPath = hdfs;
		this.conf = conf;
	}
	
	public static FileDao getHdfsObj() {
		if(hdfsObj == null){
			return new FileDao(config());
		}
		return hdfsObj;
	}
	
	public static int getCount(Page<FilesOrDirs> page) {
		int count = 0;
		try {
			count = count(page);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return count;
	}
	
	public static List<FilesOrDirs> findByPage(Page<FilesOrDirs> page) {
		List<FilesOrDirs> filesList = null;
		try {
			filesList = getHdfsObj().ls(page);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return filesList;
	}
	
	public static JobConf config(){
		JobConf conf = new JobConf(FileDao.class);
		conf.setJobName("HdfsDAO");
		conf.addResource("hadoop/core-site.xml");
		conf.addResource("hadoop/hdfs-site.xml");
		conf.addResource("hadoop/mapred-site.xml");
		return conf;
	}
	
	public static int count(Page<FilesOrDirs> page){
		int count = 0;
		try {
			Path path = new Path(Constants.HDFS+page.getCondition().get("path"));
			FileSystem fs = FileSystem.get(URI.create(hdfsPath), conf);
			FileStatus[] fileArray = fs.listStatus(path);
			if(fileArray != null){
				filesList = new ArrayList<FilesOrDirs>();
				for (FileStatus f : fileArray) {count++;}
			}
			fs.close();
		} catch (Exception e) {
			return count;
		}
		return count;
	}
	
	public List<FilesOrDirs> ls(Page<FilesOrDirs> page){
		int count = 1;
		try {
			Path path = new Path(Constants.HDFS+page.getCondition().get("path"));
			FileSystem fs = FileSystem.get(URI.create(hdfsPath), conf);
			FileStatus[] fileArray = fs.listStatus(path);
			if(fileArray != null){
				filesList = new ArrayList<FilesOrDirs>();
				if(fileArray.length>page.getStart()){
					for(int i = page.getStart(); i<fileArray.length; i++){
						count++;
						if(count<page.getLimit()){
							FilesOrDirs fd = new FilesOrDirs();
							fd.setId(Identities.uuid());
							fd.setName(fileArray[i].getPath().getName());
							fd.setGroup(fileArray[i].getGroup());
							fd.setModificationTime(DateUtils.timestamptoString(fileArray[i].getModificationTime()));
							fd.setOwner(fileArray[i].getOwner());
							fd.setPermission(fileArray[i].getPermission().toString());
							fd.setSize(String.format("%d", fileArray[i].getLen()));
							fd.setType(fileArray[i].isDirectory()?Constants.IS_DIR:Constants.IS_FILE);
							fd.setDirs(fileArray[i].getPath().toString().substring(Constants.HDFS.length()));
							filesList.add(fd);		
						}
					}
				}
			}
			fs.close();
		} catch (IOException e) {
			return filesList;
		}
		return filesList;
	}
	
	public static RetObj fileUp(String localFile, String remoteDir) {
		
		try {
			FileSystem fs = FileSystem.get(URI.create(hdfsPath), conf);
			fs.copyFromLocalFile(new Path(localFile), new Path(remoteDir));
			fs.close();
		} catch (Exception e) {
			return RetObj.getRetObj(false,Constants.MSG_UPLOAD_FAILED,2,1000);
		}
		return RetObj.getRetObj(true,Constants.MSG_UPLOAD_SUCCESS,1,1000);
	}

	public static RetObj fileDown(String remoteFile, String targetDir) {
		
		try {
			FileSystem fs = FileSystem.get(URI.create(remoteFile),conf);
			FSDataInputStream fsdi = fs.open(new Path(remoteFile));
			if(CreateFileUtil.createDir(targetDir)){
				OutputStream output = new FileOutputStream(targetDir+remoteFile.substring(remoteFile.lastIndexOf("/")));
				IOUtils.copyBytes(fsdi,output,4096,true);
			}
			fs.close();
		} catch (Exception e) {
			return RetObj.getRetObj(false,Constants.MSG_DOWNLOAD_FAILED,2,1000);
		}
		return RetObj.getRetObj(true,Constants.MSG_DOWNLOAD_SUCCESS,1,1000);
	}

	public static RetObj fileOrDirDel(String remoteFileOrDir) {
		
		try {
			Path path = new Path(remoteFileOrDir);
			FileSystem fs = FileSystem.get(URI.create(hdfsPath), conf);
			fs.deleteOnExit(path);
			fs.close();
		} catch (Exception e) {
			return RetObj.getRetObj(false,Constants.MSG_CRUD_FAILED,2,1000);
		}
		return RetObj.getRetObj(true,Constants.MSG_CRUD_SUCCESS,1,1000);
	}
	
}

Page类

package com.ebd.application.common.Base;

import java.util.List;
import java.util.Map;

public class Page<T> {

	/** 总记录数 */
    private int total;
    /** 分页结果 */
    private List<T> dataList;
    /** 开始页码 */
    private int start;
    /** 每页多少 */
    private int limit;
    /** 查询条件 */
    private Map<String,Object> condition;
    
    private int currentPage;    //当前页
    private int currentIndex;   //当前记录起始索引
    private int totalPage;      //总页数
    
    public Page(int start, int limit) {
    	this.start = start;
    	this.limit = limit;
	}
    
    public Page(int start, int limit, Map<String,Object> condition) {
    	this.start = start;
    	this.limit = limit;
    	this.condition = condition;
    }

    public int getCurrentPage() {
        if(currentPage<=0) currentPage = 1;
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getCurrentIndex() {
    	this.currentIndex = (getCurrentPage()-1)*getLimit();
        if(currentIndex<0) currentIndex = 0;
        return currentIndex;
    }

    public void setCurrentIndex(int currentIndex) {
        this.currentIndex = currentIndex;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
    	if(total%limit==0)
            totalPage = total/limit;
        else
            totalPage = total/limit+1;
        this.total = total;
    }

	public List<T> getDataList() {
		return dataList;
	}

	public void setDataList(List<T> dataList) {
		this.dataList = dataList;
	}

	public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public Map<String, Object> getCondition() {
	return condition;
    }

    public void setCondition(Map<String, Object> condition) {
	this.condition = condition;
    }

}

效果图

转载于:https://www.cnblogs.com/eRrsr/p/8367993.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值