ufserver文件上传服务器-用来管理你项目中的图片-性能可比Java、Python高十倍不止

大家在做项目的时候如果是web项目,项目又不大的情况下上传图片一般都是上传到项目的webapp目录下面,webapp下面会有一个upload的文件夹,今天给大家分享的是在项目中如何把图片上传到其他服务器,由于java比较笨重,故我用GO语言写了一个小型的上传文件的服务,下面给大家讲解具体怎么使用。

微云链接:链接:https://share.weiyun.com/5Uqsa4l 密码:9wpied

下载后是一个ufserver.exe文件,打开cmd,在命令行中执行ufserver.exe 就可以了,或者直接双击运行。执行成功如下:

如上界面,就一定启动成功啦。

下面附上一个测试的html文件。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<form action="http://localhost:9876/upload" enctype="multipart/form-data" method="post">
			<input type="file" name="upload-file" />
			<input type="submit" value="提交" />
		</form>
		
	</body>
</html>

默认的端口是9876,当然你也可以在配置文件中修改成自己的端口,配置文件下面会详细讲,

这是上传成后返回的信息如下:

{
    "absolutePath": "http://192.168.0.103:9876/2019/09/06/150405-B7z3v113.jpg",
    "createTime": "2019-09-06T11:58:42.0186458+08:00",
    "fileSize": 3047,
    "msg": "successfully",
    "originalFileName": "2.jpg",
    "relativePath": "/2019/09/06/150405-B7z3v113.jpg",
    "statusCode": 200,
    "thums": [
        "http://localhost:9876/2019/09/08/150405-kj0s66Xa_200_200_3.png"
        "http://localhost:9876/2019/09/08/150405-kj0s66Xa_120_120_3.png"
    ]  //>= v6版本中新增,缩略图
}

一些具体的信息都在里边,有绝对路径,相对路径,源文件名等,大家注意了,一般数据库里边保存的相对路径哦,千万不要保存绝对路径,除非是富文本编辑器里边上传图片的时候会保存成绝对路径。不过后面如果静态资源迁移,那就是灾难性的。

上面已经演示了如何上传,下面我们来说说它具体的配置。

第一次运行只会生成static目录,logs目录为服务的请求日志,cfg.json是配置文件,默认可以不需要cfg.json,可以直接运行

cfg.json具体配置如下:

{
  "url":"http://localhost",   //url为访问的路径,如果不是本电脑,可以配置其他IP或者域名  
  "port":9876,   //访问的端口,默认为9876
  "indexFolder":true,  //是否开启目录索引,默认为true
  "logEnable":true   //是否开启访问日志,默认为false,
  "staticDirs":["xxx","D:\\tmp\\vvv"], //v2版本中新增
  "rewriteUrls": [
       {"/resource":"img"}
    ],  //v3新增
  "defStaticDir":"static", //默认文件存放的目录为static目录
  "enableRate":false, //是否开启限流,开启后默认为1s/10000
  "capacity":10000,  //限流采用令牌桶算法,桶的容量
  "rate":10000,    //限流采用令牌桶算法,每秒的速率,建议和capacity一样
  "tempPartDir":"C:\\users\\xx", 分片上传时临时文件目录,默认为用户的TMP目录
  "isDelSearchDir": false, // v9新增 删除的时候是否开去多目录检索
  "delSearchDir":["img","static"] // v9新增 删除文件要检索哪些目录,会去这些文件夹下找对应的文件删除
  "webHookEnabled": false, // v10版本新增 启用webhook
  "webHookUrl": "http://localhost:9876/webhook",  // v10版本新增 webhook地址,默认POST请求
  "encrypt":false,  //[>=v11]是否开启访问效验
  "encryptSalt":"abcdefg1234567a1b2c3d4f5g6",  //[>=v11]效验混合加密盐值
  "encryptMethod":"md5",  //[>=v11]加密方式
  "encryptType":"dynamic" //[>=v11] dynamic 、 server  ,默认dynamic类型
  "encryptUrl" : "http://www.xxxxx.com/vs" //[>=12] 验证sign的url,一般为应用服务器自己的
}

////////////staticDirs参数说明///////////////////
假如您现在的根目录是 D:/tmp,upload-server启动后会生成D:/tmp/static,staticDirs可以让ufserver增加搜索目录,数组中的xxx是相对D:/tmp目录,数组里边可以传相对路径,也可以传绝对路径。
假如有xxx目录下有aa.js ,vvv目录下面bb.js ,那么在浏览器中访问http://localhost:9876/aa.js去会xxx
目录下查找,访问bb.js会去vvv目录下面查找,数组中越靠前优先级越大。

//////////////////rewriteUrls参数说明//////////////
rewriteUrls是一个数组,数组里边是map,键值对形式,他的作用主要是用来url重写,现在配置的有
/resource,img 意思是当我访问/resource/xxx.js 的时候,upload-server会去img目录下面找xxx.js
访问/resource/js/jq.js 会去img目录下面的js目录找jq.js


//////////////////encrypt参数说明//////////////
encrypt表示是否开启效验模式,如果开启后,默认会拦截/upload和/delete请求,会对请求进行判断是否带有加密后的sign值。
效验需要参数
-randomstr(随机字符串)
-timestamp(unix时间戳10为)
-sign(加密后的值)
客户端在上传的时候需要把这个三个值在URL参数中传过来,其中randomstr建议生成uuid,
保证每次不一致,timestamp为时间戳,但是不是当前时间的时间戳,比如说当前的这个sign值有效期是3分
钟,那么timestamp就是3分钟后的时间戳,sign是在应用服务器的服务端加密后的值,sign的加密方式为:
String result = sign+randomstr+timestamp+salt
在对result进行ASCII按从小到大的顺序排序,把排序后的结果进行md5加密
注意上面提到的salt值是在ufserver配置文件中配置,在应用服务器中保存一份,不可泄露。
/upload?sign=61f93003d3b104876558b7064a0ce40b&randomstr=sdf
&timestamp=1569826200
====================ufserver处理结果如下=======================
 timestamp:1569826200 
 sign:61f93003d3b104876558b7064a0ce40b 
 salt:abcdefg1234567a1b2c3d4f5g6 
 method:md5 
 encryptStr:00111222233445556666789aabbccdddefffggs 
 result:5e269c3040a6c16d038a9b9e79719b45 
从这个结果中我们可以看到,客户端传过来的是 timestamp、randomstr在加上配置的salt,排序后的结果
为encryptStr,在用encryptStr进行md5加密,加密后的值和传过的sign值进行比对,如果一致,就通过。如果返回错误信息。


//////////////////encryptUrl参数说明//////////////
encryptType如果为server的时候,说明客户端传过来的sign,ufserver会回调encryptUrl并且带上sign参
数和action类型,去请求应用服务器,由应用服务器验证并且返回状态,如果成功返回字符串successfully,
如果sign已经失效,返回非successfully就可以。
server的验证模式为应用服务器生成sign值,sign可以是UUID也可以是其他信息加密而成返回给客户端,客户
端上传文件到ufserver的时候,在url后面带上sign值,ufserver接受到后会以POST请求去请求在ufserver
中配置的encryptUrl,并会在encryptUrl后面以问号参数拼接sign值还有action请求,应用服务器收到后验
证sign是否有效,并且返回对应的状态值。


//////////////////encryptType参数说明//////////////
dynamic:动态加密,可以设置过期时间,一并传递给ufserver,调试相对来说麻烦一些,ufserver参与
加密,按照一定的加密方式来加密验证。
server:服务器验证加密,应用服务器生成sign传递给客户端,客户端传递给ufserver,ufserver发起
回调URL去应用服务器验证,ufserver不参与验证,全部交给应用服务器。


//////////////////IsDelSearchDir参数说明//////////////
如果IsDelSearchDir为true,返回的格式为

{
    "msg": "successfully",
    "notFiles": [
        "img:/2019/09/21/150405-16237U35.png",
        "static:/2019/09/21/150405-16237U35.png"
    ],
    "statusCode": 200,
    "successs": [
        "img:/2019/09/21/150405-7914ZGwv.png"
    ]
}

如果IsDelSearchDir为false,返回的格式为

{
    "msg": "successfully",
    "notFiles": [
    ],
    "statusCode": 200,
    "successs": [
        "/2019/09/21/150405-7B3ZxiR5.png"
    ]
}

如果文件在对应的目录下没有找到,则在notFiles中返回,删除成功会在successs中返回。
如果开启多目录检索,"img:/2019/09/21/150405-16237U35.png",会在前面加目录名,如果未开启,直接
返回对应的路径。

URL参数详解

上传文件的URL为:/upload

[URL可选参数]
  filePath  -- 如果自定义路径的话可以传这个参数,格式为 image/xxxx ,不以斜杠开头哦
  sign  [>=12] --  (可选)加密参数值
  rename    -- 是否需要重命名,默认为true,建议为true
  cfname[>=v8]   -- 自定义文件名称,比如文件上传后名称为 xxx.jpg
  thum  [>=v6] -- 是否需要生成缩略图,参数为true和false,默认为false
  async [>=v6] -- 生成缩略图异步生成还是同步执行,默认为true,经测试1s能生成>1000张以上的缩略图
  mode  [>=v6] -- 生成缩略图模式,参数有1、2、3,2为调整大小,会缩放图片,1和3位裁剪和等比缩放,具体可以传不同的参数来看效果
  q     [>=v6] -- 生成缩略图的参数,传参形式为:width,height或者width,height|width,height,例如
http://localhost:9876?thum=true&q=300,200这个url会成300*200大小的缩略图,
http://localhost:9876?thum=true&q=300,200|500,600会生成2个缩略图,分别是300*200和500*600
http://localhost:9876?thum=true&q=300,200|500,600|xx,xx|xx,xx 可以生成多重
http://localhost:9876?thum=true&q=300|500|xx|如果是正方形,可以传一个参数
http://localhost:9876?thum=true&q=300,0|200,0高度传0会按照宽度等比缩放

**cfname自定义名称必须要在rname=true的情况下才生效哦**

---------------------------------------------------

分片上传URL为:/upload/part

[URL可选参数]
  filePath -- 如果自定义路径的话可以传这个参数,格式为 image/xxxx ,不以斜杠开头哦
  rname    -- 是否需要重命名,默认为true,建议为true
  cfname   -- 自定义文件名称,比如文件上传后名称为 xxx.jpg
  rpart    -- 是否需要对part文件进行检测重写,建议为false
  sign  [>=12] 加密参数值
  
 [POST参数]
   fileId -- 文件ID,必传,由客户端保持唯一,可以使用UUID,ufserver根据fileId来生成临时目录
   chunks -- 分块数,总共有多少个块  采用webuploader 参数引用
   chunk  -- 当前的块索引,采用webuploader 参数引用
   size   -- 文件的大小字节数

 [分配上传成功后返回结果]
 
  {
    absolutePath: "http://localhost:9876"
    createTime: "2019-09-10T14:25:49.90321+08:00"
    fileSize: 3000000
    msg: "part successfully"
    originalFileName: "syncthing-windows-amd64-v1.2.1.zip"
    relativePath: ""
    statusCode: 200
    thums: null
  }

[最后一个分片上传成功后返回结果]

  {
    absolutePath: "http://localhost:9876/doc/stn.zip"
    createTime: "2019-09-10T14:25:50.5484782+08:00"
    fileSize: 404187
    msg: "successfully"
    originalFileName: "syncthing-windows-amd64-v1.2.1.zip"
    relativePath: "/doc/stn.zip"
    statusCode: 200
    thums: null
  }


---------------------------------------------------

刪除文件URL为:/delete

[URL可选参数]
  fp[>=v9]  -- 要删除文件的相对路径,上传成功后返回的相对路径,如果要删除多个,用逗号分隔,比如
http://localhost:9876/delete?fp=/2019/09/21/150405-7B3ZxiR5.png,/2019/09/21/150405-7B3ZxiR2.png
http://localhost:9876/delete?fp=/2019/09/21/150405-7B3ZxiR5.png

各种URL效果图如下:https://blog.csdn.net/qq_24434671/article/details/100625771

大家可能对目录索引不太了解,下面我们来看一下,不开启目录索引的情况下,访问http://localhost:9876,如下:

会出现拒绝访问,下面我们开启目录索引在访问:

可以看到出来了2019,这个2019是怎么来的呢,是static中的目录,static下面有多少目录,在这里都会一一列举出来,大家如果在apache网站上下载过一些jar包之类的,应该就对这类目录索引很熟悉了。

默认上传文件后保存的路径是   yy/mm/dd/hhmmss-8位随机数.后缀

如果想自定义路径怎么办呢,可以这么做:

在form中上传的url后面加参数

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<form action="http://localhost:9876/upload?filePath=static/img/2019" enctype="multipart/form-data" method="post">
			<input type="file" name="upload-file" />
			<input type="submit" value="提交" />
		</form>
		
	</body>
</html>

在URL后面可以通过filePath参数去传自定义目录http://localhost:9876/upload?filePath=static/images/2019,发现有filepath参数后程序会自动创建你传过的目录路径,注意,这里的filePath的路径是相对于static目录的,就是启动程序后会生成static目录,你传的不管目录是什么,最后都会在static目录下面创建相对目录。如果传的是上面的路径,最后在static目录下面生成的目录结构为

static/static/images/2019

如果不传filePath的话,那么就采取默认的路径规则。

关于自定义文件名

细心的同学可能发现上传后的文件名都是经过重新编制的,那么有些情况下我就想上传原来的文件名并且让程序不要修改,怎么做呢,其实可以在url后面加rename=false参数,具体如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<form action="http://localhost:9876/upload?rename=false" enctype="multipart/form-data" method="post">
			<input type="file" name="upload-file" />
			<input type="submit" value="提交" />
		</form>
		
	</body>
</html>

这样一来的话上传的时候是什么文件名,返回的就是什么文件名称了,不过这样不建议用,因为如果文件重复的话会进行覆盖,比较危险,不建议这样去传。

关于WebUploader上传案例

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" type="text/css" href="css/webuploader.css" />
		<script type="text/javascript" src="js/jquery.js"></script>
		<script type="text/javascript" src="js/webuploader.min.js"></script>
	</head>
	<body>
		<div id="uploader" class="wu-example">
			<!--用来存放文件信息-->
			<div id="thelist" class="uploader-list"></div>
			<div class="btns">
				<div id="picker">选择文件</div>
			</div>
		</div>
		<script>
			var uploader = WebUploader.create({
				auto:true,
				// swf文件路径
				swf:'js/Uploader.swf',

				// 文件接收服务端。
				server: 'http://localhost:9876/upload',
				/* fileVal:"upload-file", */
				fileVal:"upload-file",
				// 选择文件的按钮。可选。
				// 内部根据当前运行是创建,可能是input元素,也可能是flash.
				pick: '#picker',

				// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
				resize: false
			})
			uploader.on( 'uploadSuccess', function( file ) {
			   console.log(file)
			});
		</script>
	</body>
</html>

可以看到,上传成功了。

关于WebUploader分片上传案例

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" type="text/css" href="css/webuploader.css" />
		<script type="text/javascript" src="js/jquery.js"></script>
		<script type="text/javascript" src="js/webuploader.min.js"></script>
	</head>
	<body>
		<div id="uploader" class="wu-example">
			<!--用来存放文件信息-->
			<div id="thelist" class="uploader-list"></div>
			<div class="btns">
				<div id="picker">选择文件</div>
			</div>
		</div>
		<script>
			var uploader = WebUploader.create({
				auto:true,
				// swf文件路径
				swf:'js/Uploader.swf',

				// 文件接收服务端。
				server: 'http://localhost:9876/upload/part?filePath=doc&rname=false&cfname=stn.zip',
				/* fileVal:"upload-file", */
				fileVal:"upload-file",
				// 选择文件的按钮。可选。
				// 内部根据当前运行是创建,可能是input元素,也可能是flash.
				pick: '#picker',
				
				chunked:true, //开启分配上传
				chunkSize:3000000, //默认1M
				chunkRetry:2, //允许自动重试的次数
				threads:3, //上传的并发数
				formData:{"fileId":"xvvx"},

				// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
				resize: false
			})
			uploader.on( 'uploadSuccess', function( file,resp ) {
			   console.log(file,resp)
			});
			
			// 文件上传过程中创建进度条实时显示。
			uploader.on( 'uploadProgress', function( file, percentage ) {
				console.log(file,percentage)
			});
			
		</script>
	</body>
</html>
chunked:true, //开启分配上传
chunkSize:3000000, //默认1M
chunkRetry:2, //允许自动重试的次数
threads:3, //上传的并发数
formData:{"fileId":"xvvx"},

可以看到,比较重要的就是这几个配置了,前面四个用于开启和配置webuploader的分片上传

 

可以看到,我上传了一个将近10M左右的,webuploader自动分成了四个分片,浏览器发了8个请求,其中四个是options请求,四个是POST请求,(截图只截了个七个请求),浏览器把10M的文件分成四次分别提交给ufserver,ufserver收到后会在临时目录下为每个chunk创建一个文件,最后把各个小文件合并成一个大文件。 

关于ufserver的命令行参数

在命令行输入 ufserver.exe  --help

可以看到上面有几个参数选项,

  • -sp 是source path
  • -dp 是 dpath,生成缩略图后保存的文件名(全路径哦)
  • -w  宽度
  • -h  高度
  • -m 模式

以上的选项是在生成缩略图的时候需要用到,注意,如果只传-sp 的话,那么生成后的缩略图的路径是和sp在同目录下,命名是原文件名_w_h.后缀

  • -s start  用于启动服务器
  • -v 查看当前的版本号

关于用Java代码上传文件到ufserver

需要用到jar包如下:

  • org.apache.httpcomponents:httpclient:4.5.3
  • org.apache.httpcomponents:httpmime:4.5.3

jar包微云地址如下:链接:https://share.weiyun.com/5Lz7Ego 密码:j5rqgz

pom文件依赖:

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.3</version>
</dependency>

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpmime</artifactId>
	<version>4.5.3</version>
</dependency>

Java上传代码如下:

import java.io.File;
import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class UploadTest {

	public static void main(String[] args) {
		
		String URL = "http://localhost:9876/upload";
		String filePath = "C:\\Users\\xxx\\Desktop\\HW\\tp\\TCP.png";
		System.out.println(upload(URL,filePath));
	}

	/**
	 * 上传文件
	 */
	public static String upload(String url,String filePath) {
		String res = "";
		CloseableHttpClient httpclient = HttpClients.createDefault();
		try {
			HttpPost httppost = new HttpPost(url);

			RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(200000).setSocketTimeout(200000)
					.build();
			httppost.setConfig(requestConfig);

			FileBody bin = new FileBody(new File(filePath));
			StringBody comment = new StringBody("This is comment", ContentType.TEXT_PLAIN);

			//这里的name值为upload-file
			HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("upload-file", bin)
					.addPart("comment", comment).build();

			httppost.setEntity(reqEntity);

			System.out.println("executing request " + httppost.getRequestLine());
			CloseableHttpResponse response = httpclient.execute(httppost);
			try {
				System.out.println(response.getStatusLine());
				HttpEntity resEntity = response.getEntity();
				if (resEntity != null) {
					String responseEntityStr = EntityUtils.toString(response.getEntity());
					System.out.println(responseEntityStr);
					res = responseEntityStr;
					System.out.println("Response content length: " + resEntity.getContentLength());
				}
				EntityUtils.consume(resEntity);
			} finally {
				response.close();
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				httpclient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return res;
	}
}

控制台结果如下:

executing request POST http://localhost:9876/upload HTTP/1.1
HTTP/1.1 200 OK
{"absolutePath":"http://localhost:9876/2019/09/20/150405-43ehh0Sb.png","createTime":"2019-09-20T16:44:20.0764262+08:00","fileSize":233095,"msg":"successfully","originalFileName":"TCP.png","relativePath":"/2019/09/20/150405-43ehh0Sb.png","statusCode":200,"thums":null}
Response content length: 267

上面说了这么多,如果您是一般使用,没有特殊要求的话就不需要cfg.json文件,直接把ufserver下载下来运行就行。不需要其他的配置。上传文件时候的name名称是upload-file

上面就是关于ufserver.exe全部的配置和使用方式了,如果您在使用中有什么问题的话可以在评论区留言哦。

发布了106 篇原创文章 · 获赞 101 · 访问量 56万+

猜你喜欢

转载自blog.csdn.net/qq_24434671/article/details/100576661
今日推荐