sunyj 9 лет назад
Родитель
Сommit
dd8a619280

+ 43 - 0
src/main/java/com/uas/report/controller/ResourceController.java

@@ -0,0 +1,43 @@
+package com.uas.report.controller;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.apache.http.client.ClientProtocolException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.uas.report.model.Resource;
+import com.uas.report.service.ResourceService;
+
+@Controller
+@RequestMapping("/resources")
+public class ResourceController {
+	@Autowired
+	private ResourceService resourceService;
+
+	@RequestMapping("/sync")
+	@ResponseBody
+	public List<Resource> syncResources(String userName)
+			throws ClientProtocolException, URISyntaxException, IOException {
+		return resourceService.syncResources(userName);
+	}
+
+	@RequestMapping("")
+	@ResponseBody
+	public List<Resource> getResources(String folderPath)
+			throws ClientProtocolException, URISyntaxException, IOException {
+		return resourceService.getRemoteResources(folderPath);
+	}
+
+	@RequestMapping("/download")
+	@ResponseBody
+	public String downloadFile(String filePath, String mimeType, String exportPath)
+			throws ClientProtocolException, URISyntaxException, IOException {
+		resourceService.downloadFile(filePath, mimeType, exportPath);
+		return "已下载文件至" + exportPath;
+	}
+}

+ 147 - 0
src/main/java/com/uas/report/model/Resource.java

@@ -0,0 +1,147 @@
+package com.uas.report.model;
+
+import java.util.Date;
+
+/**
+ * 文件、文件夹等资源
+ * 
+ * @author sunyj
+ * @since 2016年9月22日 下午7:59:14
+ */
+public class Resource {
+
+	/**
+	 * 资源版本
+	 */
+	private Integer version;
+
+	/**
+	 * 权限
+	 */
+	private Integer permissionMask;
+
+	/**
+	 * 资源创建时间
+	 */
+	private Date creationDate;
+
+	/**
+	 * 资源最后修改时间
+	 */
+	private Date updateDate;
+
+	/**
+	 * 资源名称(只用于显示,不用作标识)
+	 */
+	private String label;
+
+	/**
+	 * 描述信息
+	 */
+	private String description;
+
+	/**
+	 * 在资源库中的路径
+	 */
+	private String uri;
+
+	/**
+	 * 资源类型
+	 */
+	private String resourceType;
+
+	public static final String FOLDER_RESOURCE_TYPE = "folder";
+
+	/**
+	 * jrxml文件类型
+	 */
+	public static final String JRXML_RESOURCE_TYPE = "jrxml";
+
+	/**
+	 * jrxml文件MIME Type
+	 */
+	public static final String JRXML_MIME_TYPE = "application/jrxml";
+
+	/**
+	 * 图片MIME Type
+	 */
+	public static final String IMAGE_MIME_TYPE = "image/*";
+
+	public Integer getVersion() {
+		return version;
+	}
+
+	public void setVersion(Integer version) {
+		this.version = version;
+	}
+
+	public Integer getPermissionMask() {
+		return permissionMask;
+	}
+
+	public void setPermissionMask(Integer permissionMask) {
+		this.permissionMask = permissionMask;
+	}
+
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	public Date getUpdateDate() {
+		return updateDate;
+	}
+
+	public void setUpdateDate(Date updateDate) {
+		this.updateDate = updateDate;
+	}
+
+	public String getLabel() {
+		return label;
+	}
+
+	public void setLabel(String label) {
+		this.label = label;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getUri() {
+		return uri;
+	}
+
+	public void setUri(String uri) {
+		this.uri = uri;
+	}
+
+	public String getResourceType() {
+		return resourceType;
+	}
+
+	public void setResourceType(String resourceType) {
+		this.resourceType = resourceType;
+	}
+
+	/**
+	 * @return 资源是否为文件夹
+	 */
+	public boolean isFolder() {
+		return resourceType.equals(FOLDER_RESOURCE_TYPE);
+	}
+
+	@Override
+	public String toString() {
+		return "Resource [label=" + label + ", description=" + description + ", uri=" + uri + ", resourceType="
+				+ resourceType + "]";
+	}
+
+}

+ 60 - 0
src/main/java/com/uas/report/service/ResourceService.java

@@ -0,0 +1,60 @@
+package com.uas.report.service;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.apache.http.client.ClientProtocolException;
+
+import com.uas.report.model.Resource;
+
+/**
+ * 管理报表模板、图片等资源
+ * 
+ * @author sunyj
+ * @since 2016年9月23日 下午5:23:49
+ */
+public interface ResourceService {
+
+	/**
+	 * 同步jasperserver repository中指定账套的资源至本地
+	 * 
+	 * @param userName
+	 *            账套名
+	 * @return 需要同步的资源
+	 * @throws IOException
+	 * @throws URISyntaxException
+	 * @throws ClientProtocolException
+	 */
+	public List<Resource> syncResources(String userName)
+			throws ClientProtocolException, URISyntaxException, IOException;
+
+	/**
+	 * 从jasperserver获取指定路径下的关于所有资源的信息
+	 * 
+	 * @param folderPath
+	 *            指定路径
+	 * @return 资源列表的信息
+	 * @throws IOException
+	 * @throws URISyntaxException
+	 * @throws ClientProtocolException
+	 */
+	public List<Resource> getRemoteResources(String folderPath)
+			throws ClientProtocolException, URISyntaxException, IOException;
+
+	/**
+	 * 从jasperserver下载文件
+	 * 
+	 * @param filePath
+	 *            文件路径
+	 * @param mimeType
+	 *            mime类型
+	 * @param exportPath
+	 *            导出到本地的路径
+	 * @throws ClientProtocolException
+	 * @throws URISyntaxException
+	 * @throws IOException
+	 */
+	public void downloadFile(String filePath, String mimeType, String exportPath)
+			throws ClientProtocolException, URISyntaxException, IOException;
+}

+ 306 - 0
src/main/java/com/uas/report/service/impl/ResourceServiceImpl.java

@@ -0,0 +1,306 @@
+package com.uas.report.service.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.uas.report.model.Resource;
+import com.uas.report.service.ResourceService;
+import com.uas.report.support.JasperserverRestAPIConf;
+
+/**
+ * 管理报表模板、图片等资源
+ * 
+ * @author sunyj
+ * @since 2016年9月23日 下午5:23:42
+ */
+@Service
+public class ResourceServiceImpl implements ResourceService {
+
+	@Autowired
+	private JasperserverRestAPIConf jsRestAPIConf;
+
+	private Logger logger = Logger.getLogger(getClass());
+
+	@Override
+	public List<Resource> syncResources(String userName)
+			throws ClientProtocolException, URISyntaxException, IOException {
+		logger.info("Synchronizing resources...");
+		List<Resource> synchronizingResources = getSynchronizingResources(userName);
+		if (!CollectionUtils.isEmpty(synchronizingResources)) {
+			for (Resource synchronizingResource : synchronizingResources) {
+				downloadFile(synchronizingResource);
+			}
+		}
+		logger.info("Synchronized");
+		return synchronizingResources;
+	}
+
+	/**
+	 * 与本地进行比较,返回需要同步的资源
+	 * 
+	 * @param resources
+	 *            用于比较的资源
+	 * @param userName
+	 *            账套名
+	 * @return 需要进行同步的资源
+	 * @throws IOException
+	 * @throws URISyntaxException
+	 * @throws ClientProtocolException
+	 */
+	private List<Resource> getSynchronizingResources(String userName)
+			throws ClientProtocolException, URISyntaxException, IOException {
+		List<Resource> synchronizingResources = new ArrayList<>();
+		List<Resource> remoteResources = getRemoteResources("/" + userName);
+		if (CollectionUtils.isEmpty(remoteResources)) {
+			return synchronizingResources;
+		}
+
+		List<Resource> localResources = getLocalResources(userName);
+		for (Resource remoteResource : remoteResources) {
+			if (needSynchronized(remoteResource, localResources)) {
+				synchronizingResources.add(remoteResource);
+			}
+		}
+		return synchronizingResources;
+	}
+
+	/**
+	 * 比较远程资源是否需要同步
+	 * 
+	 * @param remoteResource
+	 *            jasperserver库里的资源
+	 * @param localResources
+	 *            本地路径下的资源列表
+	 * @return 远程资源是否需要同步
+	 */
+	private boolean needSynchronized(Resource remoteResource, List<Resource> localResources) {
+		// 如果资源不是文件夹,并且不含"."(无后缀名),不进行比较,直接认为不需要同步
+		if (!remoteResource.isFolder() && !remoteResource.getUri().contains(".")) {
+			logger.error("Resource is invalid: " + remoteResource + "\n");
+			return false;
+		}
+
+		for (Resource localResource : localResources) {
+			if (remoteResource.getUri().equals(localResource.getUri())) {
+				// 如果远程资源比本地资源更新
+				if (remoteResource.getUpdateDate().after(localResource.getUpdateDate())) {
+					return true;
+				} else {
+					return false;
+				}
+			}
+		}
+		// 本地并没有该资源
+		return true;
+	}
+
+	/**
+	 * 获取本地指定账套下的所有资源
+	 * 
+	 * @param userName
+	 * @return 资源列表
+	 */
+	private List<Resource> getLocalResources(String userName) {
+		return getLocalResources(new File(jsRestAPIConf.getLocalBaseDir() + File.separator + userName));
+	}
+
+	/**
+	 * 获取本地指定路径下的所有资源
+	 * 
+	 * @param dir
+	 *            指定的路径,可能是文件夹或文件
+	 * @return 资源列表
+	 */
+	private List<Resource> getLocalResources(File dir) {
+		List<Resource> resources = new ArrayList<>();
+		if (dir == null || !dir.exists()) {
+			return resources;
+		}
+		if (dir.isDirectory()) {
+			// 递归获取所有资源
+			File[] files = dir.listFiles();
+			for (File file : files) {
+				resources.addAll(getLocalResources(file));
+			}
+		}
+		resources.add(convertToResource(dir));
+		return resources;
+	}
+
+	/**
+	 * 将本地文件(夹)转为Resource对象
+	 * 
+	 * @param file
+	 *            本地文件(夹)
+	 * @return 转换的资源
+	 */
+	private Resource convertToResource(File file) {
+		Resource resource = new Resource();
+		resource.setLabel(file.getName());
+		resource.setUpdateDate(new Date(file.lastModified()));
+		// 替换"\"为"/"
+		String absolutePath = file.getAbsolutePath().replace("\\", "/");
+		// 去除文件绝对路径中前半部分,只保留自账套开始的路径
+		resource.setUri(absolutePath.replace(jsRestAPIConf.getLocalBaseDir(), ""));
+		return resource;
+	}
+
+	@Override
+	public List<Resource> getRemoteResources(String folderPath)
+			throws ClientProtocolException, URISyntaxException, IOException {
+		HttpGet httpGet = new HttpGet();
+		// 返回json类型数据
+		httpGet.setHeader("Accept", "application/json");
+		List<NameValuePair> parameters = new ArrayList<>();
+		// 设置连接参数
+		parameters.add(new BasicNameValuePair("folderUri", folderPath));
+		HttpResponse response = sendRequest(httpGet, null, parameters);
+		InputStream inputStream = response.getEntity().getContent();
+		String jsonStr = IOUtils.toString(inputStream);
+		// 将返回的json数据格式化为Resource列表
+		List<Resource> resources = JSONObject.parseArray(JSONObject.parseObject(jsonStr).getString("resourceLookup"),
+				Resource.class);
+		inputStream.close();
+		return resources;
+	}
+
+	/**
+	 * 根据资源的信息下载相应文件
+	 * 
+	 * @param resource
+	 *            资源信息
+	 * @throws ClientProtocolException
+	 * @throws URISyntaxException
+	 * @throws IOException
+	 */
+	private void downloadFile(Resource resource) throws ClientProtocolException, URISyntaxException, IOException {
+		// 如果资源是文件夹,在本地创建
+		if (resource.isFolder()) {
+			new File(jsRestAPIConf.getLocalBaseDir() + resource.getUri()).mkdirs();
+			return;
+		}
+
+		String localBaseDir = jsRestAPIConf.getLocalBaseDir();
+		// 如果本地资源根路径最后以资源分隔符结尾
+		if (localBaseDir.endsWith(File.separator)) {
+			localBaseDir = localBaseDir.substring(0, localBaseDir.length() - 1);
+		}
+		String uri = resource.getUri();
+		// 从资源的uri中获取账套名(如"/UAS/jrxml/Purchase.jrxml",第一个"UAS"就是账套)
+		String userName = uri.split("/")[1];
+		// 从uri中获取资源的名称(不能使用label,其只用于方便用户查看,并不能用于标识)
+		String resourceName = uri.substring(uri.lastIndexOf("/"));
+
+		StringBuilder stringBuilder = new StringBuilder();
+		stringBuilder.append(localBaseDir).append("/").append(userName);
+
+		// 下载jrxml文件(即使该远程资源并不在jrxml根路径下,比如在嵌套文件夹下),放在localBaseDir+userName+localJrxmlDir下
+		if (uri.endsWith(Resource.JRXML_RESOURCE_TYPE)) {
+			stringBuilder.append(jsRestAPIConf.getLocalJrxmlDir()).append(resourceName);
+			downloadFile(uri, Resource.JRXML_MIME_TYPE, stringBuilder.toString());
+		}
+		// 除jrxml之外的资源视为图片,放在localBaseDir+userName+localImagesDir下
+		else {
+			stringBuilder.append(jsRestAPIConf.getLocalImagesDir()).append(resourceName);
+			downloadFile(uri, Resource.IMAGE_MIME_TYPE, stringBuilder.toString());
+		}
+	}
+
+	@Override
+	public void downloadFile(String filePath, String mimeType, String exportPath)
+			throws ClientProtocolException, URISyntaxException, IOException {
+		HttpGet httpGet = new HttpGet();
+		// 设置MIME类型
+		httpGet.setHeader("MIME", mimeType);
+		HttpResponse response = sendRequest(httpGet, filePath, null);
+		// 获取文件二进制数据
+		byte[] data = IOUtils.toByteArray(response.getEntity().getContent());
+		File exportFile = new File(exportPath);
+		// 该文件所在的路径不存在,创建
+		if (!exportFile.getParentFile().exists()) {
+			exportFile.getParentFile().mkdirs();
+		}
+		FileOutputStream fos = new FileOutputStream(exportFile);
+		fos.write(data);
+		fos.close();
+		logger.info("Download resource to " + exportPath + "\n");
+	}
+
+	/**
+	 * 发送Http请求,获得结果
+	 * 
+	 * @param request
+	 *            Http请求
+	 * @param path
+	 *            资源路径
+	 * @param parameters
+	 *            参数
+	 * @return 服务器的响应结果
+	 * @throws URISyntaxException
+	 * @throws ClientProtocolException
+	 * @throws IOException
+	 */
+	private HttpResponse sendRequest(HttpRequestBase request, String path, List<NameValuePair> parameters)
+			throws URISyntaxException, ClientProtocolException, IOException {
+		HttpClient httpClient = HttpClients.createDefault();
+		request.setURI(createURI(path, parameters));
+		// 采用HTTP Basic验证
+		request.setHeader("Authorization", "Basic " + jsRestAPIConf.getAuthorization());
+		HttpResponse response = httpClient.execute(request);
+		logger.info(request.getMethod() + " " + request.getURI() + " " + response.getStatusLine());
+		return response;
+	}
+
+	/**
+	 * 创建URI
+	 * 
+	 * @param path
+	 *            资源路径
+	 * @param parameters
+	 *            参数
+	 * @return URI对象
+	 * @throws URISyntaxException
+	 */
+	private URI createURI(String path, List<NameValuePair> parameters) throws URISyntaxException {
+		StringBuilder stringBuilder = new StringBuilder();
+		// 拼接资源全路径
+		stringBuilder.append("/").append(jsRestAPIConf.getContextRoot()).append("/").append(jsRestAPIConf.getRest())
+				.append("/").append(jsRestAPIConf.getResources());
+		if (path != null) {
+			stringBuilder.append(path);
+		}
+		URIBuilder uriBuilder = new URIBuilder();
+		// 协议、主机名、端口号、资源全路径
+		uriBuilder.setScheme(jsRestAPIConf.getSchema()).setHost(jsRestAPIConf.getHost())
+				.setPort(jsRestAPIConf.getPort()).setPath(stringBuilder.toString());
+		// 设置参数
+		if (parameters != null) {
+			uriBuilder.setParameters(parameters);
+		}
+		return uriBuilder.build();
+	}
+
+}

+ 168 - 0
src/main/java/com/uas/report/support/JasperserverRestAPIConf.java

@@ -0,0 +1,168 @@
+package com.uas.report.support;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * jasperserver rest_v2 api 相关参数,通过属性文件注入
+ * 
+ * @author sunyj
+ * @since 2016年9月23日 下午4:36:29
+ */
+@Component
+public class JasperserverRestAPIConf {
+	/**
+	 * 协议
+	 */
+	@Value("#{js_rest_api.schema}")
+	private String schema;
+
+	/**
+	 * 主机名
+	 */
+	@Value("#{js_rest_api.host}")
+	private String host;
+
+	/**
+	 * 端口号
+	 */
+	@Value("#{js_rest_api.port}")
+	private Integer port;
+
+	/**
+	 * 根路径
+	 */
+	@Value("#{js_rest_api.contextRoot}")
+	private String contextRoot;
+
+	/**
+	 * rest服务路径
+	 */
+	@Value("#{js_rest_api.rest}")
+	private String rest;
+
+	/**
+	 * resources接口路径
+	 */
+	@Value("#{js_rest_api.resources}")
+	private String resources;
+
+	/**
+	 * 验证信息
+	 */
+	@Value("#{js_rest_api.authorization}")
+	private String authorization;
+
+	/**
+	 * 图片资源路径
+	 */
+	@Value("#{js_rest_api.images}")
+	private String images;
+
+	/**
+	 * 本地资源根路径
+	 */
+	@Value("#{js_rest_api.localBaseDir}")
+	private String localBaseDir;
+
+	/**
+	 * 本地资源图片路径
+	 */
+	@Value("#{js_rest_api.localImagesDir}")
+	private String localImagesDir;
+
+	/**
+	 * 本地资源jrxml模板路径
+	 */
+	@Value("#{js_rest_api.localJrxmlDir}")
+	private String localJrxmlDir;
+
+	public String getSchema() {
+		return schema;
+	}
+
+	public void setSchema(String schema) {
+		this.schema = schema;
+	}
+
+	public String getHost() {
+		return host;
+	}
+
+	public void setHost(String host) {
+		this.host = host;
+	}
+
+	public Integer getPort() {
+		return port;
+	}
+
+	public void setPort(Integer port) {
+		this.port = port;
+	}
+
+	public String getContextRoot() {
+		return contextRoot;
+	}
+
+	public void setContextRoot(String contextRoot) {
+		this.contextRoot = contextRoot;
+	}
+
+	public String getRest() {
+		return rest;
+	}
+
+	public void setRest(String rest) {
+		this.rest = rest;
+	}
+
+	public String getResources() {
+		return resources;
+	}
+
+	public void setResources(String resources) {
+		this.resources = resources;
+	}
+
+	public String getAuthorization() {
+		return authorization;
+	}
+
+	public void setAuthorization(String authorization) {
+		this.authorization = authorization;
+	}
+
+	public String getImages() {
+		return images;
+	}
+
+	public void setImages(String images) {
+		this.images = images;
+	}
+
+	public String getLocalBaseDir() {
+		return localBaseDir;
+	}
+
+	public void setLocalBaseDir(String localBaseDir) {
+		this.localBaseDir = localBaseDir;
+	}
+
+	public String getLocalImagesDir() {
+		return localImagesDir;
+	}
+
+	public void setLocalImagesDir(String localImagesDir) {
+		this.localImagesDir = localImagesDir;
+	}
+
+	public String getLocalJrxmlDir() {
+		return localJrxmlDir;
+	}
+
+	public void setLocalJrxmlDir(String localJrxmlDir) {
+		this.localJrxmlDir = localJrxmlDir;
+	}
+
+}