近来工作上不上特别忙,加上对后台java了解一点,所以就抽时间,写了一个java版本的前后端分离的跨服务器文件上传功能,包括前后端代码。

需要购买阿里云产品和服务的,点击此链接领取优惠券红包,优惠购买哦,领取后一个月内有效: https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=fp9ccf07

一、Tomcat服务器部分

1、Tomcat服务器

单独复制一份Tomcat,用来作为文件服务器

1.1 web.xml文件:

需要在该Tomcat的conf目录下的web.xml文件的大概100行添加如下几行(红框内部分):

1.2 server.xml文件:

需要在该Tomcat的conf目录下的server.xml文件做一些端口的修改

1.3 Tomcat下建立文件夹

在该Tomcat的/webapps/ROOT目录下创建一个upload目录,用来存放上传的文件

1.4 启动Tomcat服务器

以上三步做完后,就可以启动Tomcat服务器了,在Tomcat的bin目录下执行 startup.sh 脚本

二、java部分

2.1 jar包

除了其他的jar包以外,还需要以下几个jar包

commons-io-1.3.2.jar

commons-fileupload-1.2.1.jar

jersey-client-1.18.1.jar

jersey-core-1.18.1.jar

jersey-common

2.2 配置文件

2.2.1 spring-mvc.xml文件添加如下部分:

<!-- 上传文件 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <!--最大上传尺寸 5M -->
<property name="maxUploadSize" value="5242880"/>
</bean>

2.2.2 config.properties 文件添加如下部分:

#文件服务器地址
uploadHost=http://172.16.5.102:8090/
#上传的文件保存的目录
imgPath = upload/

2.3 java文件

2.3.1 Upload.java

package com.lin.utils;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile; import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource; /**
* 上传文件工具类
* @author libo
*/
public class Upload { /**
* 上传文件
* @param request
* @param response
* @param serverPath 服务器地址:(http://172.16.5.102:8090/)
* @param path 文件路径(不包含服务器地址:upload/)
* @return
*/
public static String upload(Client client, MultipartFile file, HttpServletRequest request,HttpServletResponse response, String serverPath, String path){
// 文件名称生成策略(UUID uuid = UUID.randomUUID())
Date d = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String formatDate = format.format(d);
String str = "";
for(int i=0 ;i <5; i++){
int n = (int)(Math.random()*90)+10;
str += n;
}
// 获取文件的扩展名
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
// 文件名
String fileName = formatDate + str + "." + extension;
//相对路径
String relaPath = path + fileName; String a = serverPath + path.substring(0, path.lastIndexOf("/"));
File file2 = new File(a);
if(!file2.exists()){
boolean mkdirs = file2.mkdirs();
System.out.println(mkdirs);
} // 另一台tomcat的URL(真实路径)
String realPath = serverPath + relaPath;
// 设置请求路径
WebResource resource = client.resource(realPath); // 发送开始post get put(基于put提交)
try {
resource.put(String.class, file.getBytes());
return fileName+";"+relaPath+";"+realPath;
} catch (IOException e) {
e.printStackTrace();
return "";
}
} /**
* 删除文件
* @param filePath(文件完整地址:http://172.16.5.102:8090/upload/1234.jpg)
* @return
*/
public static String delete(String filePath){
try {
Client client = new Client();
WebResource resource = client.resource(filePath);
resource.delete();
return "y";
} catch (Exception e) {
e.printStackTrace();
return "n";
}
}
}

2.3.2 controller层

package com.lin.controller;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import javax.annotation.Resource;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.VelocityConfigurerBeanDefinitionParser; import com.lin.domain.data.ResData;
import com.lin.domain.data.ResListData;
import com.lin.domain.error.Error;
import com.lin.domain.sysUser.SysUser;
import com.lin.domain.sysUser.SysUserWithDep;
import com.lin.service.SysUserService;
import com.lin.utils.Aes;
import com.lin.utils.DateTimeUtils;
import com.lin.utils.ResponseUtils;
import com.lin.utils.Upload;
import com.lin.utils.Utils;
import com.sun.jersey.api.client.Client; import net.sf.json.JSONObject; /**
* 后台用户-controller
* @author libo
*/
@Controller
@RequestMapping("/sysUser")
public class SysUserController { @Resource
private SysUserService sysUserService; @Value(value="${imgPath}") //后台图片保存地址
private String imgPath; @Value(value="${uploadHost}")
private String uploadHost; //项目host路径 /**
* 后台用户登陆功能
* @return
* @throws IOException
*/
@ResponseBody
@RequestMapping(value="/login.do", method=RequestMethod.POST)
public void login(HttpServletRequest req, HttpServletResponse res,
@RequestParam(required=true) String loginEmail,
@RequestParam(required=true) String loginPwd) throws IOException{
//代码省略
} /**
* 根据id查询用户信息(包括部门信息)
* @param req
* @param res
* @param id
* @throws IOException
*/
@ResponseBody
@RequestMapping(value="/getSysUser.do", method=RequestMethod.GET)
public void getSysUser(HttpServletRequest req, HttpServletResponse res,
@RequestParam(required=true) Integer id) throws IOException{
  //代码省略
} /**
* 更新后台用户信息
* @param req
* @param res
* @throws IOException
*/
@ResponseBody
@RequestMapping(value="/updateSysUser.do" ,method=RequestMethod.POST)
public void updateSysUser(HttpServletRequest req, HttpServletResponse res) throws IOException{
//代码省略
} /**
* 添加用户
* @param req
* @param res
* @throws IOException
*/
@ResponseBody
@RequestMapping(value="/addSysUser.do", method=RequestMethod.POST)
public void addSysUser(HttpServletRequest req, HttpServletResponse res) throws IOException {
//代码省略
} /**
* 上传用户头像
* @param request
* @param response
*/
@ResponseBody
@RequestMapping(value="uploadSysHeadImg.do", method=RequestMethod.POST)
public void uploadSysHeadImg(HttpServletRequest request,HttpServletResponse response){
JSONObject jo = new JSONObject();
try {
MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
MultipartHttpServletRequest Murequest = resolver.resolveMultipart(request);
Map<String, MultipartFile> files = Murequest.getFileMap();//得到文件map对象

       // 实例化一个jersey
Client client = new Client(); List<String> fileNameList = new ArrayList<>();
List<String> relaPathList = new ArrayList<>();
List<String> realPathList = new ArrayList<>();
for(MultipartFile pic: files.values()){
String uploadInfo = Upload.upload(client, pic, request, response, uploadHost, imgPath);
                if(!"".equals(uploadInfo)){    //上传成功
                    String[] infoList = uploadInfo.split(";");
                    fileNameList.add(infoList[0]);    //文件名
                    relaPathList.add(infoList[1]);    //相对路径
                    realPathList.add(infoList[2]);    //真实完整路径
                }else{    //上传失败
                    fileNameList.add("");
                    relaPathList.add("");
                    realPathList.add("");
                }
}
jo.put("success", 1);
jo.put("error", null);
jo.put("fileNameList", fileNameList);
jo.put("relaPathList", relaPathList);
jo.put("realPathList", realPathList);
}catch (Exception e) {
jo.put("success", 0);
jo.put("error", "上传失败");
}
ResponseUtils.renderJson(response, jo.toString());
}
}

其他的java文件省略,然后运行java项目

三、前端部分

3.1 index1-cropper图片剪切上传

<!DOCTYPE html>
<html>
<head>
<title>index1-cropper图片剪切上传</title>
<meta charset="utf-8"/>
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="lib/cropper/dist/cropper.min.css" />
</head>
<body> <!-- accept="image/*,camera":表示允许调用相册和摄像头,capture="camera":表示直接打开摄像头-->
<!--<input id="btn1" type="file" accept="image/*,camera" capture="camera" style="opacity: 0;"/>-->
<input id="btn1" type="file" accept="image/*,camera" style="opacity: 0;"/> <div>
<div>上传之前的图片</div>
<img id="face_image" style="width:50px;height:50px;border-radius: 25px;border: 1px solid #fff;"/>
<div>上传之后服务器的图片</div>
<img id="success_image" style="width:50px;height:50px;border-radius: 25px;border: 1px solid #fff;"/>
</div>
<div>
<button id="upload_btn">上传头像</button>
<button id="image_save">保存</button>
<span></span>
</div> <div class="upload-img" style="width:300px;height: 300px;">
<img src=""/>
</div> <script src="jquery.min.js"></script>
<script src="lib/cropper/dist/cropper.min.js"></script>
<script src="lib/canvas-toBlob/canvas-toBlob.js"></script> <script>
$(function() {
//触发input file
$('#upload_btn').click(function() {
console.log('模拟点击。。。');
$('#btn1').trigger('click');
}); //图片上传
var $image = $('.upload-img > img');
$image.cropper({
viewMode: 1,
// preview: '.img-preview', //不同尺寸预览区
aspectRatio: 1, //裁剪比例,NaN-自由选择区域
autoCropArea: 0.7, //初始裁剪区域占图片比例
crop: function(data) { //裁剪操作回调
console.log(data);
}
});
var fileName; //选择上传的文件名
$('#btn1').change(function(){
var file = this.files[0];
fileName = file.name;
var reader = new FileReader();
//reader回调,重新初始裁剪区
reader.onload = function(){
// 通过 reader.result 来访问生成的 DataURL
var url = reader.result;
//选择图片后重新初始裁剪区
$image.cropper('reset', true).cropper('replace', url);
};
reader.readAsDataURL(file);
}); /*
* 上传图片
*/
$('#image_save').click(function() {
var type = $image.attr('src').split(';')[0].split(':')[1]; var canVas = $image.cropper("getCroppedCanvas", {});
//将裁剪的图片加载到face_image
$('#face_image').attr('src', canVas.toDataURL());
canVas.toBlob(function(blob) {
var formData = new FormData();
formData.append("file", blob, fileName);
http://172.16.5.102:9000/index1-cropper%E5%9B%BE%E7%89%87%E5%89%AA%E5%88%87%E4%B8%8A%E4%BC%A0.html
$.ajax({
type: "POST",
url: 'http://172.16.5.102:8081/ssm_project/sysUser/uploadSysHeadImg.do',
data: formData,
contentType: false, //必须
processData: false, //必须
dataType: "json",
success: function(res){
//清空上传文件的值
$('#btn1').val('');
$('#success_image').attr('src', res.realPathList[0]);
},
error : function() {
//清空上传文件的值
$('#btn1').val('');
}
});
}, type);
}); //取消
$("#image_cancel").click(function() {
//清空上传文件的值
$(_pageId + inputFileId).val('');
});
});
</script>
</body>
</html>

效果图:

3.2 index2-jquery-Form表单提交

<!DOCTYPE html>
<html lang="en">
<head>
<!-- 选择图片后,就开始上传 -->
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>index2-jquery-Form表单提交</title>
<script src="jquery.min.js"></script>
<script src="jquery.form.js"></script>
</head>
<body>
<img height="100" id="success_img"/>
<form id="jvForm" action="o_save.shtml" method="post" enctype="multipart/form-data">
<!-- 保存图片的相对路径,方便提交给后台,存到数据库 -->
<input type="hidden" name="" id="img_path"/> <input type="file" id="select_file" onchange="uploadPic()" name="pic12"/>
</form> <script>
//上传图片
function uploadPic(){
//定义参数
var options = {
url:"http://172.16.5.102:8081/ssm_project/sysUser/uploadSysHeadImg.do",
type:"post",
dataType:"json",
success:function(res){
$('#success_img').attr('src', res.realPathList[0]); //真实完整路径
$('#img_path').val(res.relaPathList[0]); //相对路径
}
};
//jquery.form使用方式
$("#jvForm").ajaxSubmit(options);
}
</script>
</body>
</html>

效果图:

3.3 index3-jquery-ajax提交

<!DOCTYPE html>
<html lang="en">
<head>
<!-- 选择图片后,需要点击提交按钮,才开始上传,可以不需要form标签 -->
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>index3-jquery-ajax提交</title>
<script src="jquery.min.js"></script>
</head>
<body>
<img id="success_img" height="100px">
<br> <input type="file" id="upload_file"> <!-- 保存图片的相对路径,方便提交给后台,存到数据库 -->
<input type="hidden" name="" id="img_path"/>
<input type="button" id="uploadPicButton" value="上传"> <div>
<a href="" id="download" download=""></a>
</div>
<script>
//上传图片
$('#uploadPicButton').click(function () {
var pic = $('#upload_file')[0].files[0];
var fd = new FormData();
fd.append('uploadFile', pic);
$.ajax({
url:"http://172.16.5.102:8081/ssm_project/sysUser/uploadSysHeadImg.do",
type:"post",
data: fd, // Form数据
cache: false,
contentType: false,
processData: false,
success:function(res){
$('#success_img').attr('src', res.realPathList[0]); //真实完整路径
$('#img_path').val(res.relaPathList[0]); //相对路径 $('#download').html(res.fileNameList[0]);
$('#download').attr('href', res.realPathList[0]);
$('#download').attr('download', res.fileNameList[0]);
}
});
})
</script>
</body>
</html>

效果图:

3.4 index4-WebUploader多文件上传

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>index4-WebUploader多文件上传</title>
<link rel="stylesheet" href="lib/webuploader/dist/webuploader.css">
<script src="jquery.min.js"></script>
<script src="lib/webuploader/dist/webuploader.js"></script>
<style>
.file-item {
display: inline-block;
text-align: center;
font-size: 12px;
margin: 10px;
}
.file-item .progress {
position: relative;
height: 6px;
width: 100%;
background-color: #ddd;
}
.file-item .progress span {
position: absolute;
left: 0;
top: 0;
width: 0%;
height: 6px;
background-color: dodgerblue;
}
.suc-img-item {
display: inline-block;
height: 100px;
margin: 10px;
}
</style>
</head>
<body> <h3>图片上传</h3>
<!--dom结构部分-->
<div id="uploader-demo">
<h4>上传前缩略图</h4>
<div id="fileList" class="uploader-list"></div>
<div id="upInfo"></div> <h4>上传后预览图</h4>
<div id="successImgList">
<!--<img class="suc-img-item" src="http://172.16.5.102:8090/upload/4337bd72-57d3-4d26-b94e-61193e9fe440.jpg">-->
<!--<img class="suc-img-item" src="http://172.16.5.102:8090/upload/4337bd72-57d3-4d26-b94e-61193e9fe440.jpg">-->
</div> <div id="filePicker">选择文件</div>
</div>
<input type="button" id="btn" value="开始上传">
<script>
// // 图片上传demo
$(function() {
var $ = jQuery,
$list = $('#fileList'),
$successImgList = $('#successImgList'),
ratio = window.devicePixelRatio || 1, // 优化retina, 在retina下这个值是2 (window.devicePixelRatio是设备上物理像素和设备独立像素(device-independent pixels (dips))的比例)
thumbnailWidth = 50 * ratio, // 缩略图宽度
thumbnailHeight = 50 * ratio, // 缩略图高度
uploader; // Web Uploader实例 // 初始化WebUploader
uploader = WebUploader.create({
auto: false, // 自动上传
swf:'../js/Uploader.swf', // swf文件路径
server: 'http://172.16.5.102:8081/ssm_project/sysUser/uploadSysHeadImg.do', // 文件接收服务端接口地址
threads:'5', //同时运行5个线程传输
fileNumLimit:'10', //文件总数量只能选择10个 // 选择文件的按钮,可选
pick: {
id:'#filePicker', //选择文件的按钮
multiple:true //允许可以同时选择多个图片
}, quality: 90, // 图片质量,只有type为`image/jpeg`的时候才有效
compressSize: 0, // 单位字节,如果图片大小小于此值,不会采用压缩
crop: true, //是否同意剪切 //限制传输文件类型,accept可以不写
accept: {
title: 'Images', //描述
extensions: 'gif,jpg,jpeg,bmp,png,mkv,mp4', //文件类型
mimeTypes: '*/*' //mime类型(*/* 可以上传所有类型)
}, compress: false, //是否启用压缩
resize: false, //尺寸不改变
duplicate: false //是否允许重复上传
}); // 当有文件添加进来的时候,创建img显示缩略图使用
uploader.on('fileQueued', function(file) {
var $li = $('<div id="' + file.id + '" class="file-item thumbnail">' +
'<img>' +
'<div class="info">' + file.name + '</div>' +
'<div class="progress"><span></span></div>' +
'</div>'),
$img = $li.find('img'); $list.append($li); // $list为容器jQuery实例 // 创建缩略图
// 如果为非图片文件,可以不用调用此方法。
// thumbnailWidth x thumbnailHeight 为 50 x 50
uploader.makeThumb(file, function(error, src) {
if (error) {
$img.replaceWith('<span>不能预览</span>');
return;
}
$img.attr('src', src);
}, thumbnailWidth, thumbnailHeight);
}); // 文件上传过程中创建进度条实时显示。 uploadProgress事件:上传过程中触发,携带上传进度。 file:文件对象;percentage:传输进度 Nuber:类型
uploader.on('uploadProgress', function(file, percentage){
console.log(file);
console.log(percentage);
var $li = $('#'+file.id),
$percent = $li.find('.progress span'); // 避免重复创建
if (!$percent.length) {
$percent = $('<p class="progress"><span></span></p>').appendTo($li).find('span');
}
$percent.css('width', percentage * 100 + '%');
}); // 文件上传成功时候触发,给item添加成功class, 用样式标记上传成功。 file:文件对象, response:服务器返回数据
uploader.on('uploadSuccess', function(file,res) {
$('#'+file.id).addClass('upload-state-done');
$("#upInfo").html("<font color='red'>"+res._raw+"</font>");
$successImgList.append('<img class="suc-img-item" src="'+res.realPathList[0]+'">');
}); // 文件上传失败 file:文件对象 , code:出错代码
uploader.on('uploadError', function(file,code) {
var $li = $( '#'+file.id ),
$error = $li.find('div.error'); // 避免重复创建
if(!$error.length) {
$error = $('<div class="error"></div>').appendTo($li);
}
$error.text('上传失败!');
}); // 不管成功或者失败,文件上传完成时触发。 file: 文件对象
uploader.on('uploadComplete', function( file ) {
$('#'+file.id ).find('.progress').remove();
}); //绑定提交事件
$("#btn").click(function() {
console.log("上传...");
uploader.upload(); //执行手动提交
console.log("上传成功");
}); });
</script>
</body>
</html>

效果图:

四、查看文件服务器Tomcat下上传的文件

注:由于我是一个前端开发人员,只对后台java了解一点。如有更好的解决方案,希望大家一起讨论,共同进步。

前后端分离跨服务器文件上传-Java SpringMVC版的更多相关文章

  1. django 12天(跨域,文件上传,下载,cookie,session)

    django 12天(跨域,文件上传,下载) 跨域 什么是跨域 1.协议不同 2.端口不同 3.主机不同 如何解决跨域 1.安装django-cors-headers模块 2.在settings.py ...

  2. nginx-springboot-vue前后端分离跨域配置

    nginx-springboot-vue前后端分离跨域配置 引言 接着上篇--简单的springboot-vue前后端分离登录Session拦截的demo,其中跨域是通过springboot后端全局设 ...

  3. springMvc---跨服务器文件上传(实测总结)

    序言: 该案例是采用springMvc实现跨服务器图片上传功能,其中用到的主要类和工具有:CommonsMultipartResolver.jquery.form.js.如果要实现多个文件上传,只需要 ...

  4. netcore3.1 + vue (前后端分离) ElementUI多文件带参数上传

    vue前端代码 前端主要使用了ElementUI的el-uploda插件,除去业务代码需要注意的是使用formdata存储片上传时所需的参数 <el-upload class="upl ...

  5. ASP.NET、JAVA跨服务器远程上传文件(图片)的相关解决方案整合

    一.图片提交例: A端--提交图片 protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { string u ...

  6. nginx反向代理实现前后端分离&跨域问题

    1.代理和跨域 1.1 正向代理 1)用户希望代理服务器帮助其和要访问服务器之间实现通信,需要: a.用户IP报文的目的IP=代理服务器IP: b.用户报文端口号=代理服务器监听端口号: c.HTTP ...

  7. Django前后端分离跨域请求问题

    一.问题背景 之前使用django+vue进行前后端分离碰到跨域请求问题,跨域(域名或者端口不同)请求问题的本质是由于浏览器的同源策略导致的,当请求的响应不是处于同一个域名和端口下,浏览器不会接受响应 ...

  8. Django:前后端分离后联调给前端传数据

    实现前后端分离后,有了下面几点改变: 1.服务器一分为二,前后端分别部署,静态资源放在前端服务器,业务代码放在后的服务器 2.前端服务器需要接收Http请求(一般使用node.js) 3.前端服务器需 ...

  9. [转]腾讯云Linux云服务器文件上传利器——WinSCP

    本文转自:http://bbs.qcloud.com/thread-4379-1-1.html WinSCP简介 WinSCP是一个Windows环境下使用SSH的开源图形化SFTP客户端.同时支持S ...

随机推荐

  1. Spring 级联属性

    Spring 级联属性是当两个bean 关联时  从一个bean 给 另一个bean 赋值 Application xml  配置如下 <bean id="ZhangSan" ...

  2. 语音传输之RTP/RTCP/UDP及软件实现关键点

    语音通信是实时通信,一定要保证实时性,不然用户体验会很糟糕.IETF设计了RTP来承载语音等实时性要求很高的数据,同时设计了RTCP来保证服务质量(RTP不保证服务质量).在传输层,一般选用UDP而不 ...

  3. M方法

    ThinkPHP函数详解:M方法 M方法用于实例化一个基础模型类,和D方法的区别在于:1.不需要自定义模型类,减少IO加载,性能较好:2.实例化后只能调用基础模型类(默认是Model类)中的方法:3. ...

  4. 一个基于Asp.net MVC的博客类网站开源了!

    背景说明: 大学时毕业设计作品,一直闲置在硬盘了,倒想着不如开源出来,也许会对一些人有帮助呢,而且个人觉得这个网站做得还是不错了,毕竟是花了不少心思,希望对你有所帮助. github地址:https: ...

  5. vector 向量容器用法祥解

    vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的. 用法:         ...

  6. 我的第一个python web开发框架(4)——数据库结构设计与创建

    小白做好前端html设计后,马上开始进入数据库结构设计步骤. 在开始之前,小白回忆了一下老大在公司里培训时讲过的数据库设计解说: 对于初学者来说,很多拿到原型时不知道怎么设计数据表结构,这是很正常的事 ...

  7. 获取报告 Stream转string,利用字符串分割转换成DataTable

    protected void Button1_Click(object sender, EventArgs e) { MemoryStream stream = new MemoryStream(); ...

  8. Echarts数据可视化grid直角坐标系(xAxis、yAxis),开发全解+完美注释

    全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 Echarts数据可视化开发参数配置全解 6大公共组件详解(点击进入): title详解. tooltip详解.toolb ...

  9. 当谈到 GitLab CI 的时候,我们该聊些什么(上篇)

    "微服务"这个概念近两年非常热,正在慢慢改变 DevOps 的思路.微服务架构把一个庞大的业务系统拆解开来,每一个组件变得更加独立自治.松耦合.但是,同时也伴随着部署单元粒度越来越 ...

  10. Django实现用户密码重置

    使用Django内置的认证视图实现简单的通过邮箱重置密码的功能版本:django 1.11 在django.contrib.auth.views中提供了四个类视图用于密码重置 class Passwo ...