精华内容
下载资源
问答
  • 1.前端项目OSS存储 2.后端定时去数据库中取数据,前端请求直接从内存中取数据,减少后台与数据库之间的请求流量 3.前端使用缓存

    1.前端项目OSS存储

    2.后端定时去数据库中取数据,前端请求直接从内存中取数据,减少后台与数据库之间的请求流量

    3.前端使用缓存

    4.后端接口返回使用gzip压缩

    展开全文
  • 这几天一直在被一个问题纠结:一个stateful session bean的实例变量中保存了一个ResultSet的实例,但是,当这个实例在该bean的一个方法中被创建后,从该方法返回后却发现这个ResultSet实例被关闭了。 这个问题我...
    这几天一直在被一个问题纠结:一个stateful session bean的实例变量中保存了一个ResultSet的实例,但是,当这个实例在该bean的一个方法中被创建后,从该方法返回后却发现这个ResultSet实例被关闭了。

    这个问题我最初的第一直觉是,一定是什么地方调用了ResultSet的close方法。经过,一次又一次的调试断点可以断定没有调用这个方法。这时候觉得似乎和事务相关,觉得是某次调用抛出了异常,导致事务退出。但是,现在再想想,即使是有异常,也不是根本原因。根本原因还是事务相关。

    由于一直没有明确的思路,这个问题就放了几天一直到昨天。这次我决定把之前down掉的调试环境搭建起来。因为我的调用是从另一个虚拟机发起。现在,想想这一步也不是必须的。其实完全可以找到那个方法的调用点,打上断点就可以断定是从哪个分支过来的。

    确定了上游调用逻辑后,思路就清晰起来了: stateful session bean中定义的几个方法是业务方法,在一个上游方法中,被依次调用。问题是,第一个方法A创建并保持了一个ResultSet的实例,后续的方法B,C都依赖于这个ResultSet实例。当方法B被调用时,报了一个错:ResultSet already close。

    此时,我开始关注这个Bean的事务,它是容器管理的事务。类型是Required。上游调用方法是一个普通类的方法没有事务关联,所以,每次调用方法A就会重新发起一个事务。这时候,我想是不是因为这里的问题?每次方法返回,事务就终结于是在事务中创建的ResultSet实例就close了。

    为了验证我的想法我把事务的类型改成了Supports。这样如果调用方没有事务,会话bean的方法A就不会关联到一个事务。改完之后重起application server,运行case结果通过了。看来真是这里的问题。

    现在的疑问是这个问题是怎么引入的?联想到系统刚刚升级到了EJB3,很多配置从XML文件一道了类中。于是查看了原来的配置文件,发现原来事务的类型是Bean,但是Bean中并没有用到事务,所以相当于没有事务。现在,升级的时候把它搞错了写成了Container。

    重头梳理这个问题的解决思路,其实如果先去看改动历史应该能很快的确定问题所在。我也确实这么做了,但是只看了类文件的改动历史没有看配置文件。还是,问题定位的不准确。
    展开全文
  • 文件上传思路梳理

    2021-05-26 14:10:34
    文件上传思路梳理 最近项目里有很多关于图片上传、视频上传的功能,由于以前也只是会用个大概,总是去其他项目里找现成的代码,当 CV 工程师,完成需求的时候很没有成就感,改起来也很烦躁,就花了些时间,自己梳理...

    文件上传思路梳理

    最近项目里有很多关于图片上传、视频上传的功能,由于以前也只是会用个大概,总是去其他项目里找现成的代码,当 CV 工程师,完成需求的时候很没有成就感,改起来也很烦躁,就花了些时间,自己梳理了一下整个流程。

    下面是项目里的一个关于视频上传的完整流程

    html 页面

    这里就用修改页面了,功能完善一些,像我当初回显的问题卡了好久,还是找前端大佬来帮忙解决的

    html 页面

    <!DOCTYPE html>
    <html lang="zh" xmlns:th="http://www.thymeleaf.org" >
    <head>
        <th:block th:include="include :: header('修改视频管理')" />
        <th:block th:include="include :: bootstrap-fileinput-css" />
        <th:block th:include="include :: select2-css" />
        <th:block th:include="include :: bootstrap-select-css" />
    </head>
    <body class="white-bg">
        <div class="wrapper wrapper-content animated fadeInRight ibox-content">
            <form class="form-horizontal m" id="form-video-edit" th:object="${video}">
                <input name="videoId" th:field="*{videoId}" type="hidden">
                <div class="form-group">    
                    <label class="col-sm-3 control-label">统一播放:</label>
                    <div class="col-sm-8" style="display: flex;align-items: center">
                        <div class="form-check" style="display: flex;align-items: center">
                            <input style="margin: 0 10px 0 0"  type="radio" onchange="handleClick()" name="unified" id="exampleRadios1" value="1">
                            <label  for="exampleRadios1" style="margin: 0" ></label>
                        </div>
                        <div class="form-check" style="margin-left: 30px;display: flex;align-items: center" >
                            <input style="margin: 0 10px 0 0"  type="radio" onchange="handleClick()" name="unified" id="exampleRadios2" value="0">
                            <label for="exampleRadios2" style="margin: 0" ></label>
                        </div>
                    </div>
                </div>
                <div class="form-group" id="village">
                    <label class="col-sm-3 control-label">所在小区:</label>
                    <div class="col-sm-8">
                        <input name="village" th:field="*{village}" class="form-control" type="text" required>
                    </div>
                </div>
                <div class="form-group" id="place">
                    <label class="col-sm-3 control-label">小区内地点:</label>
                    <div class="col-sm-8">
                        <input name="place" th:field="*{place}" class="form-control" type="text" required>
                    </div>
                </div>
                <div class="form-group" id="uuId">
                    <label class="col-sm-3 control-label">硬件编码:</label>
                    <div class="col-sm-8">
                        <input name="uuId" th:field="*{uuId}" class="form-control" type="text" required>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-8">
                        <input name="videoPath" id="videoPath" th:field="*{videoPath}" class="form-control" type="text" style="display: none">
                    </div>
                </div>
                <div class="form-group" >
                    <form id="form" action="system/video/upload" method="post" enctype="multipart/form-data">
                        <div class="file-loading">
                            <input id="input-id" name="files" multiple type="file">
                        </div>
                    </form>
                </div>
            </form>
        </div>
        <th:block th:include="include :: footer" />
        <th:block th:include="include :: bootstrap-fileinput-js" />
        <th:block th:include="include :: bootstrap-select-js" />
        <th:block th:include="include :: select2-js" />
        <script th:inline="javascript">
            var prefix = ctx + "system/video";
            var collType = [[${video.unified}]]
    
            //回显视频
            var myList = new Array();   //回显文件数组
            var List = new Array();     //定义一个全局变量去接受文件名和id
            var files = document.getElementById("videoPath").value;		//获取后台传递的多个视频路径字符串
            if (files != null && files != ""){
                var fileList = files.substr(0, files.length - 1).split(";");
                for (var i = 0; i<fileList.length; i++){
                	//myList[i]:获取单个视频播放路径
                    myList[i] = `<video controls src="http://${window.location.host}${fileList[i]}" style="width:100%;height:100%;"></video>`
                    List.push({ filePath: fileList[i] + ';', KeyID: 'init_'+i })
                }
            }
    
    		回显,根据 collType 确定单选按钮的选中项,控制其他输入框是否展示
            $(function () {
                if(collType==1){
                    $('#exampleRadios1').attr('checked',true)
                    $('#uuId').hide()
                    $('#place').hide()
                    $('#village').hide()
                }else{
                    $('#exampleRadios2').attr('checked',true)
                    $('#uuId').show()
                    $('#place').show()
                    $('#village').show()
                }
            });
    
            var videoPath = "";
            $(function () {
                initFileInput("input-id");
            })
            //点击事件,通过单选按钮的值,判断其他输入框是否展示
            function handleClick() {
                var type = $('input[type="radio"][name="unified"]:checked').val()
                if(type==1){
                    $('#uuId').hide()
                    $('#place').hide()
                    $('#village').hide()
                }else{
                    $('#uuId').show()
                    $('#place').show()
                    $('#village').show()
                }
            }
            function initFileInput(ctrlName) {
                var control = $('#' + ctrlName);
                control.fileinput({
                    language: 'zh', //设置语言
                    uploadUrl: prefix + "/upload", //上传的地址
                    allowedFileExtensions: [ "mp4","mpeg","avi","navi","asf","wmv","mov","3gp","rmvb","rm","flv"],//接收的文件后缀
                    //uploadExtraData:{"id": 1, "fileName":'123.mp3'},
                    uploadAsync: true, //默认异步上传
                    showUpload: true, //是否显示上传按钮
                    showRemove : true, //显示移除按钮
                    showPreview : true, //是否显示预览
                    showCaption: false,//是否显示标题
                    browseClass: "btn btn-default", //按钮样式
                    dropZoneEnabled: true,//是否显示拖拽区域
                    maxFileSize: 0,//单位为kb,如果为0表示不限制文件大小
                    maxFileCount: 10, //表示允许同时上传的最大文件个数
                    initialPreviewAsData: false,
                    overwriteInitial: false,
                    enctype: 'multipart/form-data',
                    validateInitialCount:true,
                    previewFileIcon: "<i class='glyphicon glyphicon-king'></i>",
                    msgFilesTooMany: "选择上传的文件数量({n}) 超过允许的最大数值{m}!",
                    initialPreview: myList, //myList[i]:获取视频播放路径
                }).on('filepreupload', function(event, data, previewId, index) {     //上传中触发事件
    
                }).on("fileuploaded", function (event, data, previewId, index) {    //文件上传成功触发事件
                    if (data.response.code == 0){
                        var filePath = data.response.msg
                        List.push({ filePath: filePath, KeyID: previewId })
                        console.log('文件上传成功!');
                    }
                }).on('fileerror', function(event, data, msg) {  //一个文件上传失败触发事件
                    console.log('文件上传失败!');
                }).on("filecleared",function(event, data, msg){  //清空文件触发事件
                    for (var i = 0; i < List.length; i++) {
                        delete List[i];
                    }
                }).on("fileremoved", function (event, data, previewId, index) { //删除单个文件触发事件
                    console.log(event,'event', data,'data', previewId,'previewId', index,'index')
                    for (var i = 0; i < List.length; i++) {
                        if (List[i].KeyID== previewId || List[i].KeyID== data) {
                            List.splice(i,1);
                        }
                    }
                    console.log('文件删除!');
                }).on("filesuccessremove",function (event, previewId, extra) { //删除上传成功视频的触发事件
                    for (var i = 0; i < List.length; i++) {
                        if (List[i].KeyID== previewId) {
                            List.splice(i,1);
                        }
                    }
                //点击删除预览框中的删除按钮前触发( initialPreview中的文件除外,只针对于还未上传的文件 )  //id=文件容器的id  , index=文件容器的index
                }).on("filepreremove",function (event, previewId, extra) {
                    console.log(event, previewId, extra)
                })
            }
    
            $("#form-video-edit").validate({
                onkeyup: false,
                focusCleanup: true,
                rules:{
                    uuId:{ //硬件编码进行正则限制
                        isUUName:true
                    },
                    village:{ //小区字数限制
                        isDeptName:true
                    },
                    place:{ //地点字数限制
                        areaName:true
                    }
                }
            });
    
            function submitHandler() {
                //将数组中的文件路径拼接赋值给 videoPath ,并通过表单传递到后台
                for (var i = 0; i < List.length; i++) {
                    videoPath = videoPath + List[i].filePath;
                }
                document.getElementById("videoPath").value = videoPath;
    
                if ($.validate.form()) {
                    $.operate.save(prefix + "/edit", $('#form-video-edit').serialize());
                }
            }
        </script>
    </body>
    </html>
    

    这里用的 BootstrapFileInput 图片上传的插件,附上 fileinput 的属性作用以及详细说明
    直到最后,还有一个小问题,就是编辑的时候,直接删除回显的视频是没用的,删除事件无法触发,除非我们在文件上传框内添加一条新的视频。这个是确实找了很久也没解决掉,欢迎大佬指点🤞

    controller 类

    在我们选择了视频并点击上传后,视频会根据 uploadUrl 路径,找到 controller 里的上传方法:

    controller 类

    	/**
         * 视频上传
         */
        @PostMapping("/upload")
        @ResponseBody
        public String picUpload(@RequestParam("files") MultipartFile[] files, HttpServletRequest request, HttpServletResponse response)
        {
            StringBuffer urlString=new StringBuffer();
            AjaxResult ajaxResult = null;
            try {
                if (files != null && files.length > 0) {
                    for (int i = 0; i < files.length; i++) {
                        MultipartFile file = files[i];
                        //获取文件路径用";"拼接,返回前端
                        urlString.append(FileUploadUtils.uploadWithFileName(RuoYiConfig.getVersionAttachPath(), file)+";") ;
                    }
                }
                ajaxResult = AjaxResult.success(urlString.toString());
                return ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
            } catch (Exception e) {
                ajaxResult = AjaxResult.error(e.getMessage());
                return ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
            }
        }
    

    我们从这里开始,一步步的看,首先是将上传的视频集 files 遍历。

    ① 这里的 RuoYiConfig 是一个关于项目相关配置的类,他的 getVersionAttachPath() 方法,其实是我们自定义的一个文件保存的路径名字段 profile 也是可以在项目的 application.yml 配置文件里进行更改的,我的本地环境路径是:D:/ruoyi/uploadPath

    配置文件 RuoYiConfig

        /** 上传路径 */
        private static String profile;
    
    	public static String getProfile(){
    		return profile;
    	}
    
    	/**
         * 获取版本附件上传路径
         * @return
         */
        public static String getVersionAttachPath() {
            return getProfile() + "/versionAttach";
        }
    

    ② 这里的 FileUploadUtils 是文件上传的工具类,

    工具类 FileUploadUtils

    package com.ruoyi.common.utils.file;
    
    import java.io.File;
    import java.io.IOException;
    import org.apache.commons.io.FilenameUtils;
    import org.springframework.web.multipart.MultipartFile;
    import com.ruoyi.common.constant.Constants;
    import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
    import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
    import com.ruoyi.common.exception.file.InvalidExtensionException;
    import com.ruoyi.common.utils.DateUtils;
    import com.ruoyi.common.utils.Md5Utils;
    import com.ruoyi.common.utils.StringUtils;
    import com.ruoyi.framework.config.RuoYiConfig;
    
    /**
     * 文件上传工具类
     * 
     * @author ruoyi
     */
    public class FileUploadUtils
    {
        /**
         * 默认大小 50M
         */
        public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
    
        /**
         * 默认的文件名最大长度 100
         */
        public static final int DEFAULT_FILE_NAME_LENGTH = 100;
    
        /**
         * 默认上传的地址
         */
        private static String defaultBaseDir = RuoYiConfig.getProfile();
    
        private static int counter = 0;
    
        public static void setDefaultBaseDir(String defaultBaseDir)
        {
            FileUploadUtils.defaultBaseDir = defaultBaseDir;
        }
    
        public static String getDefaultBaseDir()
        {
            return defaultBaseDir;
        }
    
        /**
         * 根据文件路径上传,文件名不变
         *
         * @param baseDir 相对应用的基目录
         * @param file 上传的文件
         * @return 文件名称
         * @throws IOException
         */
        public static final String uploadWithFileName(String baseDir, MultipartFile file) throws IOException
        {
            try
            {
            	//通过基目录、上传文件和文件类型数组,获取文件路径
            	//baseDir 相对应用的基目录  --D:/ruoyi/uploadPath/versionAttach
            	//file 上传的文件
            	//MimeTypeUtils.DYNAMIC_ALLOWED_EXTENSION:自定义的文件类型数组
                return uploadUseFileName(baseDir, file, MimeTypeUtils.DYNAMIC_ALLOWED_EXTENSION);
            }
            catch (Exception e)
            {
                throw new IOException(e.getMessage(), e);
            }
        }
    
        /**
         * 文件上传,文件名不变
         *
         * @param baseDir 相对应用的基目录
         * @param file 上传的文件
         * @param allowedExtension 上传文件类型
         * @return 返回上传成功的文件名
         * @throws FileSizeLimitExceededException 如果超出最大大小
         * @throws FileNameLengthLimitExceededException 文件名太长
         * @throws IOException 比如读写文件出错时
         * @throws InvalidExtensionException 文件校验异常
         */
        public static final String uploadUseFileName(String baseDir, MultipartFile file, String[] allowedExtension)
                throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
                InvalidExtensionException
        {
        	//获取上传的文件名长度
            int fileNamelength = file.getOriginalFilename().length();
            //判断文件名是否超过自定义的默认文件名最大长度
            if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
            {
                throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
            }
    
    		//通过上传的文件和文件类型数组,校验文件大小、文件类型是否符合
            assertAllowed(file, allowedExtension);
    
    		// DateUtils 日期路径 即年/月/日  --2018/08/08
    		//拼接日期路径和文件名 --2018/08/08/视频.mp4
            String fileName = DateUtils.datePath() + "/" + file.getOriginalFilename();
    
    		//通过基目录和文件名,拼接路径,创建文件
            File desc = getAbsoluteFile(baseDir, fileName);
            //保存文件
            //使用此方法保存必须要绝对路径且文件夹必须已存在,否则报错
            file.transferTo(desc);
            //通过基目录和文件名,获取上传后文件的路径  --/profile/versionAttach/2018/08/08/视频.mp4
            String pathFileName = getPathFileName(baseDir, fileName);
            return pathFileName;
        }
    
    	/**
         * 拼接路径,创建文件
         *
         * @param baseDir 相对应用的基目录
         * @param fileName 日期路径和文件名
         */
        private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
        {
        	//拼接路径  --D:/ruoyi/uploadPath/versionAttach/视频.mp4
            File desc = new File(uploadDir + File.separator + fileName);
    
    		//判断创建文件的时文件所在的 文件夹 是否存在
            if (!desc.getParentFile().exists())
            {
            	//避免文件创建失败(该文件所在的文件夹不存在所以创建它所在的文件目录)
            	//mkdirs()方法 ,如果文件夹已经存在,是不会再次创建的
                desc.getParentFile().mkdirs();
            }
            //判断文件是否存在
            if (!desc.exists())
            {
            	//创建文件
                desc.createNewFile();
            }
            return desc;
        }
    
    	/**
         * 拼接文件上传后的路径
         *
         * @param baseDir 相对应用的基目录
         * @param fileName 日期路径和文件名
         */
        private static final String getPathFileName(String uploadDir, String fileName) throws IOException
        {
        	//获取系统自定义文件路径(D:/ruoyi/uploadPath)的长度  --int dirLastIndex = 20
            int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
            //截取基目录(D:/ruoyi/uploadPath/versionAttach)  --String currentDir = "versionAttach"
            String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
            //Constants.RESOURCE_PREFIX:资源映射路径  --/profile
            String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
            //String pathFileName = "/profile/versionAttach/2018/08/08/视频.mp4"
            return pathFileName;
        }
    
        /**
         * 文件大小校验
         *
         * @param file 上传的文件
         * @return
         * @throws FileSizeLimitExceededException 如果超出最大大小
         * @throws InvalidExtensionException
         */
        public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
                throws FileSizeLimitExceededException, InvalidExtensionException
        {
        	//获取文件的大小
            long size = file.getSize();
            //判断文件大小是否超过自定义默认文件大小
            if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
            {
                throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
            }
    
    		//获取文件名称  --视频.mp4
            String fileName = file.getOriginalFilename();
            //获取文件后缀名  --mp4
            String extension = getExtension(file);
            //判断文件后缀名,是否在文件类型数组里
            if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
            {
                if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
                {
                    throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                            fileName);
                }
                else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
                {
                    throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                            fileName);
                }
                else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
                {
                    throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                            fileName);
                }
                else
                {
                    throw new InvalidExtensionException(allowedExtension, extension, fileName);
                }
            }
    
        }
    
        /**
         * 判断MIME类型是否是允许的MIME类型
         *
         * @param extension
         * @param allowedExtension
         * @return
         */
        public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
        {
        	//判断文件后缀名,是否在文件类型数组里
            for (String str : allowedExtension)
            {
                if (str.equalsIgnoreCase(extension))
                {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 获取文件名的后缀
         * 
         * @param file 表单文件
         * @return 后缀名
         */
        public static final String getExtension(MultipartFile file)
        {
        	//通过FilenameUtils类,获取文件的后缀名
            String extension = FilenameUtils.getExtension(file.getOriginalFilename());
            if (StringUtils.isEmpty(extension))
            {
                extension = MimeTypeUtils.getExtension(file.getContentType());
            }
            return extension;
        }
    }
    

    从 upload 方法跳转过来后,跟着代码一步一步的走,一行一行的添加注释,最后发现其实也没有太难,只是很多方法用的少、见的少,所以写的时候很不自信,很慌。这次浅显的流程梳理,也算是使我受益匪浅了。

    附上一些其他要用到的工具类:

    媒体类型工具类 MimeTypeUtils

    package com.ruoyi.common.utils.file;
    
    /**
     * 媒体类型工具类
     * 
     * @author ruoyi
     */
    public class MimeTypeUtils
    {
        public static final String IMAGE_PNG = "image/png";
    
        public static final String IMAGE_JPG = "image/jpg";
    
        public static final String IMAGE_JPEG = "image/jpeg";
    
        public static final String IMAGE_BMP = "image/bmp";
    
        public static final String IMAGE_GIF = "image/gif";
        
        public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
    
        public static final String[] FLASH_EXTENSION = { "swf", "flv" };
    
        public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
                "asf", "rm", "rmvb" };
    
        public static final String[] DEFAULT_ALLOWED_EXTENSION = {
                // 图片
                "bmp", "gif", "jpg", "jpeg", "png",
                // word excel powerpoint
                "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
                // 压缩文件
                "rar", "zip", "gz", "bz2",
                // pdf
                "pdf" };
    
        public static final String[] DYNAMIC_ALLOWED_EXTENSION = {
                // 图片
                "bmp", "gif", "jpg", "jpeg", "png",
                //视频
                "mp4","mpeg","avi","navi","asf","wmv","mov","3gp","rmvb","rm","flv"
                };
    
        public static String getExtension(String prefix)
        {
            switch (prefix)
            {
                case IMAGE_PNG:
                    return "png";
                case IMAGE_JPG:
                    return "jpg";
                case IMAGE_JPEG:
                    return "jpeg";
                case IMAGE_BMP:
                    return "bmp";
                case IMAGE_GIF:
                    return "gif";
                default:
                    return "";
            }
        }
    }
    
    

    时间工具类 DateUtils

        /**
         * 日期路径 即年/月/日 如2018/08/08
         */
        public static final String datePath()
        {
            Date now = new Date();
            return DateFormatUtils.format(now, "yyyy/MM/dd");
        }
    

    通用常量信息 Constants

        /**
         * 资源映射路径 前缀
         */
        public static final String RESOURCE_PREFIX = "/profile";
    

    配置文件 application.yml

    # 项目相关配置
    ruoyi:
      # 文件路径 示例( Windows配置D:/ruoyi/uploadPath)
      profile: D:/ruoyi/uploadPath
    

    好事定律:每件事最后都会是好事,如果不是好事,说明还没到最后。

    展开全文
  • 今天遇到了一个有线网络问题,定位的流程花了不少时间,本来是搞网络芯片那么多年,应该对网络这块的 问题定位了如指掌才对,可是,遇到问题才发现,很多问题的解决不仅仅是靠知识的,更重要的是靠基于你知识 得...

      今天遇到了一个有线网络问题,定位的流程花了不少时间,本来是搞网络芯片那么多年,应该对网络这块的

    问题定位了如指掌才对,可是,遇到问题才发现,很多问题的解决不仅仅是靠知识的,更重要的是靠基于你知识

    得思维方式。

      具体问题是这样的,一个嵌入式单板上有两个网络,系统刚装好,不知道网口是否可以,这种情况下,该怎么办

    呢?

      ping工具是基本的手段,通过这个工具,可以判断出网络的硬件是否正常,两端是否能联得通。当ping不通的时候

    首先要看以下几个方面:

      ip地址配置是否正确,这个其实很简单,就是只要把网络的两端配置三个地方即可:

      client侧:ifconfig eth0 192.168.1.222

      server侧:ifconfig eth0 192.168.1.111

      client侧 + server侧:通过配置,确保网关和子网掩码两个系统一样,不一样的话,通过命令:

        netmask 255.255.255.0
        route add default gw 192.168.1.1

      配置一样。

      这里面要注意一个非常重要的东西,针对pc机,在ping包不通的情况下,一定要记得关闭防火墙(sudo ufw disable)试试。

      一般人以为ping包通了,就可以万事大吉了,这就大错特错了,一定要通过iperf来验证udp和tcp包是否通的,这两个不通,

    你依旧无法上网。

      UDP的具体测试方式:

    服务器端:

    iperf -u -s

    客户端:

    iperf -u -c 192.168.1.111 -b 100M -t 60

    在udp模式下,以100Mbps为数据发送速率,客户端到服务器192.168.1.1上传带宽测试,测试时间为60秒。

    iperf -u -c 192.168.1.1111 -b 5M -P 30 -t 60

    客户端同时向服务器端发起30个连接线程,以5Mbps为数据发送速率。

    iperf -u -c 192.168.1.1111 -b 100M -d -t 60

    以100M为数据发送速率,进行上下行带宽测试。

      tcp的具体测试方式:

    服务器端:

    iperf -s

    客户端:

    iperf -c 192.168.1.111 -t 60

    在tcp模式下,客户端到服务器192.168.1.1上传带宽测试,测试时间为60秒。

    iperf -c 192.168.1.111  -P 30 -t 60

    客户端同时向服务器端发起30个连接线程。

    iperf -c 192.168.1.111 -d -t 60

    进行上下行带宽测试。

      如果都ok了,才说明你的网络是ok的。

    转载于:https://www.cnblogs.com/dylancao/p/8358639.html

    展开全文
  • 最近工作有点忙,马上要放假了,今天想和大家简单分享下这几天的一些想法,如有问题,还请多多赐教。 目前安全厂商的安全策略主要是基于传统的五元组思维来梳理冗余策略、失效策略等,这种功能可以将安全运维人员...
  • 自动化测试思路梳理

    2019-06-24 12:18:02
    今天准备梳理一下自己自动化测试的思路 自动化测试的目的无非是提高测试效率,减少重复性工作,让人有时间去思考更多的技术问题 首先,做自动化测试,有两种方法,工具和代码 工具有哪些呢:robot framework,QTP,...
  • dubbo学习思路梳理

    2019-02-17 22:29:00
    dubbo要解决的问题 rpc调用需要定制。额外的工作量 分布式服务中,服务动辄几十上百,相互之间的调用错综复杂,相互依赖严重 对集群性的服务,需要负载策略 对集群性的服务,能动态扩展节点 dubbo标签 ...
  • 为的是能够利用这个虚拟但却非常实用的媒介,来赢得广大客户们的心,让他们主动的去消费并传递口碑,那积分的如何赢得与积分兑换,也成了他们所关心的首要问题。 一个企业需要耗费巨大的财力与人力来搭建及运营积分...
  • 问题 2)安装Vscode 3)打开Vscode 问题 问题1:ubuntu中root账号使用vscode (2)卸载 3、方式二:直接官网下载与安装 (1)安装 参考 1、经典数学问题 (1)斐波那契数列 1)数学特点 数列中的两项之...
  • 说明:本文主要罗列一些关于cloudstack中agent和agentAttach的运行逻辑,所写不一定正确且还有很多细节待完善,只是作为这几天梳理这一块的一个中间成果记录在案,后续会不断完善。 问题描述 前几天在197环境下...
  • 大家好,我是小z要说最有特色的数据分析师,一定要提接地气的陈老师。作为咨询顾问出身的数据分析师,陈老师的观点,是数据圈子里最接地气,最让人信服的观点。最爱具体问题具体分析的接地气的陈老师,...
  • vue-recoucervideo01 video01 live server vue简单使用及使用流程-为手写提供思路 vue.js替换页面模板 手写替换页面模板 手写替换页面模板的问题
  • 以下以neo4j作为图库,梳理一下使用图进行交互式编程的思路。我觉得neo4j作为图库可能也不是最好的选择(天然排斥java),但在小规模应用下无所谓了,neo4j还是提供了一个很友好的web管理页面。 图(库)的应用点: ...
  • 分布式-查询MySQL类算法-思路梳理

    千次阅读 2017-10-12 22:16:09
    今天刚来的问题思路很懵,就找学长老杨帮忙理理思路,说着说着,想起十一假期看到的一个知乎关于规模达到多大的数据才值得用大数据的方式处理(当时是为了查MySQL的容量还有为啥用MySQL分布式什么,还顺便得知...
  • 思路:参考ListView的addHeadView方法封装构建一个Adapter封装类,在adpter中维护一个HeaderView和FooterView数组,在onCreateViewHolder方法中为每一个HeaderView构建一个ViewHolder。        ...
  • 多线程的理解思路梳理 + synchronized与Lock 的比较理解多线程的根本:资源问题与锁的对象synchronized的理解附1:生产者消费者实现代码理解Thread和Runnable的区别附2:Thread和Runnable都可以进行资源同步控制附3...
  • 最常见的三个性能相关问题; 如何确认服务器是否达到了性能最佳的状态。 某条语句为什么执行的不够快。 如何诊断被用户描述成停顿、堆积、或者卡死的某些间歇性疑难故障。 性能常见指标 每秒查询次数。 CPU利用...
  • 梳理

    2019-05-12 12:02:24
    (一)梳理思路 搭建项目结构 搭建前端环境,技术准备,git 前后端分离的思想,开发的流程,mybatisplus的入门 商品品牌和分类,以及crud实现、高级查询。分页查询 redis项目实战、页面静态化 图片文件的统一...
  • 之前也是从grunt/gulp/fis/过来的,到现在的webpack,中间有些问题没怎么细想,比如明明是构建工具为什么调试总是要开启一个http服务之类的,现在就来简单梳理思路 最原始的构建工具无非是这样: 改动了某个资源...
  • 自己对解题思路梳理: 1.该问题初看似乎很复杂,但是经过一层层分析拆解,问题的解法便显得直观了 2.在看题目的时候应该有几个部分可以把握注意: (1)、每只青蛙每次跳跃都是等距的,这代表如果我们取两个点,便...
  • 在黑暗中摸索思考外加上网查了点资料后,思路才逐渐清晰。当前还没有深入到模型细节,只是捋顺了一些基本概念,问题拆解如下: 通俗点讲,也就是怎么将音频信号和动画人物口型建立起联系,更近一步,是为了将音素...
  • springMVC解决的问题:能够实现一个controllor 来完成,前端用户对数据库增删改查的操作。并且从前端获取数据的时候,在形参的位置放一个pojo实体,就能实现自动赋值。免除了以前 每从前端获取一个参数就需要调用...
  • ANR 问题一般解决思路

    2020-04-24 14:59:44
    本文案例主要源于项目上实际遇到的问题,希望通过梳理之后能够对ANR问题能够快速定位,减少排查时间,同时在遇到棘手问题,能够更加从容。 先说下三种常见类型 1:KeyDispatchTimeout(谷歌default 5s,MTK平台上是....
  • **研究问题:**随着电影行业逐渐国际化,电影的展示方式也逐渐统一,大多数的电影,特别是国内的电影,都采用中英文双字字幕的展示方式,而国外电影的引进,也都在后期添加了中英文双字的字幕,这为广大英语爱好者...
  • 做OC所遇到的问题进行梳理

    千次阅读 2019-02-27 14:49:13
    问题解决思路:认为是缓存的问题,通过重启Xcode,删除掉APP,手机重新启动,重新安装。(问题得以解决) 通过在网上搜索找到了解决方法: 1.修改启动图的图片名称; 2.将图片的目录放在文件的目录下面(如果放在...
  • ANR问题一般解决思路

    2019-08-29 11:24:02
    本文案例主要源于项目上实际遇到的问题,希望通过梳理之后能够对ANR问题能够快速定位,减少排查时间,同时在遇到棘手问题,能够更加从容。 先说下三种常见类型 1:KeyDispatchTimeout(谷歌default 5s,MTK平台上是...
  • 这是公司研发团队发现的一个关于...归根揭底还是对开发框架和技术应用的把握上存在纰漏,但个人觉得在分析问题->找出原因->确认解决方案这一思路和策略上本文能起到一定借鉴作用,所以稍微梳理了一下拿出来和大家分享。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,265
精华内容 506
关键字:

梳理问题思路