diff --git a/.gitignore b/.gitignore index da05752..1b5050f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ /Feign/src/main/resources/application-test.yml /Feign/src/main/resources/bootstrap-test.yml /Gateway/src/main/resources/application-test.yml +/ResourceManager/src/main/resources/application-test.yml +/ResourceManager/src/main/resources/bootstrap-test.yml diff --git a/ResourceManager/pom.xml b/ResourceManager/pom.xml new file mode 100644 index 0000000..2f45d41 --- /dev/null +++ b/ResourceManager/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + cn.crtech.cloud.resource_manager + ResourceManager + 1.0.1 + + + + cn.crtech.cloud.dependencies + Dependencies + 1.0.1 + + + + + + 8 + 8 + 1.8 + UTF-8 + + 3.13.2 + 7.0.2 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + + javax.servlet + javax.servlet-api + provided + + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springframework.boot + spring-boot-starter-data-redis + + + io.lettuce + lettuce-core + + + + + redis.clients + jedis + + + org.springframework + spring-test + + + + org.apache.commons + commons-lang3 + + + + cn.crtech.cloud.common + Common + 1.0.1 + + + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun.oss.version} + + + + + io.minio + minio + ${minio.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + -Dfile.encoding=UTF-8 + + + + + diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/ResmanagerApplcation.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/ResmanagerApplcation.java new file mode 100644 index 0000000..5cb09a2 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/ResmanagerApplcation.java @@ -0,0 +1,33 @@ +package cn.crtech.cloud.resmanager; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import tk.mybatis.spring.annotation.MapperScan; + +//微服务启动时使用 begin + +@EnableDiscoveryClient +@SpringBootApplication +@MapperScan("cn.crtech.cloud.resmanager.mapper") +public class ResmanagerApplcation { + public static void main(String[] args) { + SpringApplication.run(ResmanagerApplcation.class, args); + } +} + +//微服务启动时使用 end + +//使用launcher启动时使用 begin +//launcher.NacosConfig @Component需要放开 +// +//@SpringBootApplication +//@MapperScan("cn.crtech.cloud.resmanager.mapper") +//public class ResmanagerApplcation extends SpringBootServletInitializer { +// @Override +// protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { +// return application.sources(ResmanagerApplcation.class); +// } +//} + +//使用launcher启动时使用 end diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/AliYunOSSConfig.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/AliYunOSSConfig.java new file mode 100644 index 0000000..5e3959e --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/AliYunOSSConfig.java @@ -0,0 +1,18 @@ +package cn.crtech.cloud.resmanager.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "resmanager.aliyunoss") +public class AliYunOSSConfig { + private String serverUrl; + + private String accessKey; + + private String secretKey; + + private String bucketName; +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/LocalConfig.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/LocalConfig.java new file mode 100644 index 0000000..b859ef9 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/LocalConfig.java @@ -0,0 +1,14 @@ +package cn.crtech.cloud.resmanager.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "resmanager.local") +public class LocalConfig { + private String path; + + private String bucketName; +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/MinioConfig.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/MinioConfig.java new file mode 100644 index 0000000..c2201ba --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/MinioConfig.java @@ -0,0 +1,26 @@ +package cn.crtech.cloud.resmanager.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "resmanager.minio") +public class MinioConfig { + private String endpoint; + + private Integer port; + + private String accessKey; + + private String secretKey; + + private String bucketName; + + private Boolean secure; + + private String accessType; + + private Integer urlExpires; +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/RedisRepositoryConfig.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/RedisRepositoryConfig.java new file mode 100644 index 0000000..64d4311 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/config/RedisRepositoryConfig.java @@ -0,0 +1,30 @@ +package cn.crtech.cloud.resmanager.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * Redis相关配置 + */ +@Configuration +@EnableRedisRepositories +public class RedisRepositoryConfig { + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(connectionFactory); + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringRedisSerializer); + redisTemplate.setHashKeySerializer(stringRedisSerializer); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/controller/FileController.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/controller/FileController.java new file mode 100644 index 0000000..b4763bc --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/controller/FileController.java @@ -0,0 +1,181 @@ +package cn.crtech.cloud.resmanager.controller; + +import cn.crtech.cloud.common.api.CommonResult; +import cn.crtech.cloud.common.utils.ThumbnailatorUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONObject; +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.model.ObjectMetadata; +import com.aliyun.oss.model.PutObjectResult; +import io.minio.MinioClient; +import io.minio.PutObjectOptions; +import io.minio.errors.*; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * Author : yj + * Date : 2021-03-19 + * Description: + */ +@RestController +@RequestMapping("/file") +public class FileController { + private static String endpoint = "http://oss-cn-chengdu.aliyuncs.com"; + private static String accessKeyId = "LTAI5tGBETbnyAS6tAxULF8M"; + private static String accessKeySecret = "lJppRT678UVMWJr9EBaJfbKdoZ7eBl"; + private static String bucketName = "crtechtest"; + + @RequestMapping("/u1") + public CommonResult uploadFileTOALiYUN(HttpServletRequest request, HttpServletResponse response) { + OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); + List files = ((MultipartHttpServletRequest) request).getFiles("file"); + files.stream().forEach(file -> { + System.out.println(file.getOriginalFilename()); + //System.out.println(file.getBytes()); + System.out.println(file.getName()); + System.out.println(file.getContentType()); + System.out.println(file.getSize()); + String fileKey = file.getOriginalFilename(); + System.out.println(); + try { + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType("image/jpg"); + + PutObjectResult putObjectResult = ossClient.putObject(bucketName, fileKey, file.getResource().getInputStream(), objectMetadata); + // 设置URL过期时间为10年 3600l* 1000*24*365*10 + Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 10); + URL url = ossClient.generatePresignedUrl(bucketName, fileKey, expiration); + + + System.out.println(putObjectResult.getETag()); + System.out.println(putObjectResult.getVersionId()); + System.out.println(putObjectResult.getRequestId()); + System.out.println(putObjectResult.getServerCRC()); + System.out.println(url); + + + } catch (IOException e) { + ossClient.shutdown(); + e.printStackTrace(); + } + ossClient.shutdown(); + + }); + + + return CommonResult.success(null); + } + + @RequestMapping("/u") + public CommonResult uploadFileTOMINIO(HttpServletRequest request, HttpServletResponse response) throws Exception { + + try { + // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象 + MinioClient minioClient = new MinioClient("http://localhost:9000", "minio", "KWbPmRsTWre2X2X6"); + + String bucketName = "bucket1"; + // 检查存储桶是否已经存在 + boolean isExist = minioClient.bucketExists(bucketName); + if (isExist) { + System.out.println("Bucket already exists."); + } else { + // 创建一个名为asiatrip的存储桶,用于存储照片的zip文件。 + minioClient.makeBucket(bucketName); + JSONObject jsonObject = new JSONObject(); + ArrayList> object = new ArrayList>(); + Map Map1 = new HashMap<>(); + String[] str = new String[]{"s3:GetBucketLocation", "s3:ListBucket"}; + Map1.put("Action", str); + Map1.put("Effect", "Allow"); + Map1.put("Principal", "*"); + Map1.put("Resource", "arn:aws:s3:::" + bucketName); + object.add(Map1); + Map Map2 = new HashMap<>(); + Map2.put("Action", "s3:GetObject"); + Map2.put("Effect", "Allow"); + Map2.put("Principal", "*"); + Map2.put("Resource", "arn:aws:s3:::" + bucketName + "/myobject*"); + object.add(Map2); + jsonObject.set("Statement", object); + jsonObject.set("Version", "2012-10-17"); + String policyJson = jsonObject.toString(); + minioClient.setBucketPolicy(bucketName, policyJson); + } + + String bucketPolicy = minioClient.getBucketPolicy(bucketName); + + System.out.println(bucketPolicy); + + + List files = ((MultipartHttpServletRequest) request).getFiles("file"); + String objectUrl = null; + for (MultipartFile file : files) { + //InputStream inputStream1 = file.getInputStream(); + String fileKey = file.getOriginalFilename(); + String fileType = fileKey.split(".")[1]; + System.out.println(fileKey); + //System.out.println(file.getBytes()); + System.out.println(file.getName()); + System.out.println(file.getContentType()); + System.out.println(file.getSize()); + PutObjectOptions putObjectOptions = new PutObjectOptions(-1, 5L * 1024 * 1024); + putObjectOptions.setContentType("image/jpeg;charset=UTF-8"); +// putObjectOptions.setContentType(); +// putObjectOptions.setHeaders(); + + // 使用putObject上传一个文件到存储桶中。 + + try { + + InputStream inputStream = file.getResource().getInputStream(); + + byte[] bytes = ThumbnailatorUtil.compressImageWithWH(file.getBytes(), 256, 256); + + minioClient.putObject(bucketName, IdUtil.simpleUUID() + "." + fileType, inputStream, putObjectOptions); + objectUrl = minioClient.getObjectUrl(bucketName, fileKey); + System.out.println("====objectUrl===" + objectUrl); + } catch (ErrorResponseException e) { + e.printStackTrace(); + } catch (InsufficientDataException e) { + e.printStackTrace(); + } catch (InternalException e) { + e.printStackTrace(); + } catch (InvalidBucketNameException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (InvalidResponseException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (XmlParserException e) { + e.printStackTrace(); + } + } +// files.stream().forEach(file -> { +// +// +// }); + Map retMap = new HashMap<>(); + retMap.put("url", objectUrl); + return CommonResult.success(retMap); + } catch (Exception e) { + throw e; + } + } +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/controller/ResourceController.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/controller/ResourceController.java new file mode 100644 index 0000000..08921d5 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/controller/ResourceController.java @@ -0,0 +1,242 @@ +package cn.crtech.cloud.resmanager.controller; + +import cn.crtech.cloud.common.api.CommonResult; +import cn.crtech.cloud.common.dto.Result; +import cn.crtech.cloud.resmanager.pojo.Resource; +import cn.crtech.cloud.resmanager.service.ResourceService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/") +public class ResourceController { + @Autowired + private ResourceService service; + + @PostMapping("/list") + public CommonResult list(@RequestBody List fileIds) throws Exception { + List resources = service.listByIds(fileIds); + return CommonResult.success(resources); + } + + @PostMapping("/remove") + public CommonResult remove(@RequestBody List fileIds) throws Exception { + return CommonResult.success(service.remove(fileIds)); + } + + @PostMapping("/commit") + public CommonResult commit(@RequestBody List fileIds) throws Exception { + return CommonResult.success(service.commit(fileIds)); + } + + @PostMapping("/rollback") + public CommonResult rollback(@RequestBody List fileIds) throws Exception { + return CommonResult.success(service.rollback(fileIds)); + } + + /** + * 获取文件实际显示访问url + * + * @param fileId 文件ID + * @param response 返回结果 + * @throws Exception 文件不存在/解析异常 + */ + @GetMapping("/s/{fileId}") + public void showUrl(@PathVariable(value = "fileId") String fileId, + final HttpServletResponse response) throws Exception { +// return CommonResult.success(service.getUrl(fileId, false)); + response.sendRedirect(service.getUrl(fileId, false)); + } + + @GetMapping("/getUrl/{fileId}") + public CommonResult getUrl(@PathVariable(value = "fileId") String fileId, + final HttpServletResponse response) throws Exception { + return CommonResult.success(service.getUrl(fileId, false)); + } + + /** + * @param params 参数 + *
    + *
  • list: 文件ID集合
  • + *
+ * @return 返回查询结果 + */ + @PostMapping("/s/much") + public CommonResult showMuchUrl(@RequestBody Map params) throws Exception { + List list = (List) params.get("list"); + if (list.size() > 0) { + return CommonResult.success(service.getMuchUrl(list, false)); + } else { + return CommonResult.failed("参数为空"); + } + } + + /** + * 获取文件实际下载url + * + * @param fileId 文件ID + * @param response 返回数据对象集合 + * @throws Exception 可能存在文件不存在等异常 + */ + @GetMapping("/d/{fileId}") + public void downloadUrl(@PathVariable(value = "fileId") String fileId, + final HttpServletResponse response) throws Exception { + response.sendRedirect(service.getUrl(fileId, true)); + } + + /** + * 使用本地链接访问时显示或下载文件流 + * 本地存储时:最后建议使用nginx做转发 + * TODO: MinIO和AliYunOSS时由于要获取流,可能要考虑做二级资源缓存,然后使用nginx做转发 + * + * @param contentType1 + * @param contentType2 + * @param request + * @param response + * @return + * @throws Exception + * @Params ** path: /ls/erp/prescription/202305111110183b5464d7ce964493a1e0a7714109cc48 + */ + @GetMapping({"/res/{contentType1}/{contentType2}/**", "/down/{contentType1}/{contentType2}/**"}) + public ResponseEntity getSteam( + @PathVariable(value = "contentType1") String contentType1, + @PathVariable(value = "contentType2") String contentType2, + final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + + boolean download = request.getRequestURI().startsWith("/down"); + + response.setContentType(contentType1 + "/" + contentType2); + if (download) { + response.setHeader("Content-Disposition", "attachment"); + } + + String relativePath = "/" + URLDecoder.decode(request.getRequestURI().substring(((download ? "/down/" : "/res/") + contentType1 + "/" + contentType2 + "/").length()), "UTF-8"); + + StreamingResponseBody stream = outputStream -> { + InputStream inputStream = null; + try { + inputStream = service.getStreamByPath(relativePath); + byte[] bytes = new byte[1024]; + int len; + while ((len = inputStream.read(bytes)) != -1) { + outputStream.write(bytes, 0, len); + } + outputStream.flush(); + } catch (Exception e) { + throw new IOException(e); + } finally { + if (inputStream != null) inputStream.close(); + if (outputStream != null) outputStream.close(); + } + }; + return new ResponseEntity(stream, HttpStatus.OK); + } + + @PostMapping("/upload") + public CommonResult addFile( + @RequestParam(name = "file") MultipartFile file, + @RequestParam(name = "path") String path, + @RequestParam(name = "name") String name, + @RequestParam(name = "description") String description) throws Exception { + Resource resource = service.addFile(file, path, name, description); + return CommonResult.success(resource); + } + + /** + * 拷贝文件 + * + * @param fileId + * @return + * @throws Exception + */ + @GetMapping("/copyOfMinIoFile") + public Result copyOfMinIoFile(@RequestParam("fileId") String fileId) throws Exception { + return Result.success(service.copyOfMinIoFile(fileId)); + } + + /** + * 通过文件id获取文件流 + * + * @param fileId + * @return + */ + @GetMapping("/getFileStream") + public ResponseEntity getFileStream(@RequestParam("fileId") String fileId) { + StreamingResponseBody stream = outputStream -> { + InputStream inputStream = null; + try { + inputStream = service.getStreamByFileId(fileId); + byte[] bytes = new byte[1024]; + int len; + while ((len = inputStream.read(bytes)) != -1) { + outputStream.write(bytes, 0, len); + } + outputStream.flush(); + } catch (Exception e) { + throw new IOException(e); + } finally { + if (inputStream != null) inputStream.close(); + outputStream.close(); + } + }; + return new ResponseEntity(stream, HttpStatus.OK); + } + + /** + * 使用本地链接访问时显示或下载文件流 + * 本地存储时:最后建议使用nginx做转发 + * TODO: MinIO和AliYunOSS时由于要获取流,可能要考虑做二级资源缓存,然后使用nginx做转发 + * + * @param request + * @param response + * @return + * @throws Exception + * @Params ** path: /ls/erp/prescription/202305111110183b5464d7ce964493a1e0a7714109cc48 + */ + @GetMapping({"/byte/res/stream/{fileId}", "/byte/down/stream/{fileId}"}) + public CommonResult getFileStreamByteByFileId( + @PathVariable(name = "fileId") String fileId, + final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + boolean download = request.getRequestURI().startsWith("/down"); + if (download) { + response.setHeader("Content-Disposition", "attachment"); + } + Resource resource = service.get(fileId); + String relativePath = URLDecoder.decode(resource.getPath() + "/" + resource.getName(), "UTF-8"); + InputStream inputStream = service.getStreamByPath(relativePath); + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int num = inputStream.read(buffer); + while (num != -1) { + baos.write(buffer, 0, num); + num = inputStream.read(buffer); + } + baos.flush(); + Map fileMap = new HashMap<>(); + fileMap.put("fileByteArray", baos.toByteArray()); + return CommonResult.success(fileMap, "获取成功"); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + + } +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/general/UserController.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/general/UserController.java new file mode 100644 index 0000000..d889923 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/general/UserController.java @@ -0,0 +1,14 @@ +package cn.crtech.cloud.resmanager.general; + +import cn.crtech.cloud.common.api.CommonResult; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.Map; + +@FeignClient("crtech-cloud-general") +public interface UserController { + @RequestMapping("/user/info") + CommonResult getUserInfo(@RequestBody Map map); +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/holder/LoginUserHolder.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/holder/LoginUserHolder.java new file mode 100644 index 0000000..7af32c1 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/holder/LoginUserHolder.java @@ -0,0 +1,42 @@ +package cn.crtech.cloud.resmanager.holder; + +import cn.crtech.cloud.common.dto.UserDto; +import cn.hutool.core.convert.Convert; +import cn.hutool.json.JSONObject; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +/** + * 获取登录用户信息 + * Created by macro on 2020/6/17. + */ +@Component +public class LoginUserHolder { + public UserDto getCurrentUser() { + //从Header中获取用户信息 + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userStr = null; + UserDto userDto = new UserDto(); + try { + if (!StringUtils.isBlank(request.getHeader("user"))) { + userStr = URLDecoder.decode(request.getHeader("user"), "UTF-8"); + JSONObject userJsonObject = new JSONObject(userStr); + userDto.setCompanyCode(userJsonObject.getStr("company_code")); + userDto.setUserName(userJsonObject.getStr("user_name")); + userDto.setId(Integer.valueOf(userJsonObject.getStr("id"))); + userDto.setRoles(Convert.toList(String.class, userJsonObject.get("authorities"))); + return userDto; + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/mapper/ResourceMapper.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/mapper/ResourceMapper.java new file mode 100644 index 0000000..d4def74 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/mapper/ResourceMapper.java @@ -0,0 +1,10 @@ +package cn.crtech.cloud.resmanager.mapper; + +import cn.crtech.cloud.resmanager.pojo.Resource; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.additional.idlist.SelectByIdListMapper; +import tk.mybatis.mapper.common.Mapper; + +@Repository +public interface ResourceMapper extends Mapper, SelectByIdListMapper { +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/pojo/Resource.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/pojo/Resource.java new file mode 100644 index 0000000..468f583 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/pojo/Resource.java @@ -0,0 +1,46 @@ +package cn.crtech.cloud.resmanager.pojo; + +import lombok.Data; + +import javax.persistence.*; +import java.util.Date; + +@Data +@Table(name = "resources") +public class Resource { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "select uuid()") + private String id; + + @Column(name = "mime_type") + private String mimeType; + @Column(name = "name") + private String name; + @Column(name = "size") + private Long size; + @Column(name = "path") + private String path; + @Column(name = "description") + private String description; + @Column(name = "create_time") + private Date createTime; + @Column(name = "create_user") + private Integer createUser; + @Column(name = "last_modify_time") + private Date lastModifyTime; + @Column(name = "last_modify_user") + private Integer lastModifyUser; + @Column(name = "is_temp") + private Integer isTemp; + + /** + * 1 插入 + * 2 修改(基本没用) + * 3 删除 + */ + @Column(name = "temp_oper") + private Integer tempOper; + + @Transient + private String url; +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/ResmanagerServer.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/ResmanagerServer.java new file mode 100644 index 0000000..edadbfe --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/ResmanagerServer.java @@ -0,0 +1,74 @@ +package cn.crtech.cloud.resmanager.server; + +import cn.crtech.cloud.resmanager.pojo.Resource; +import cn.crtech.cloud.resmanager.server.impl.AliYunOSSStorage; +import cn.crtech.cloud.resmanager.server.impl.LocalStorage; +import cn.crtech.cloud.resmanager.server.impl.MinioStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotNull; +import java.io.InputStream; + +@Component +public class ResmanagerServer implements ApplicationRunner { + @Value("${resmanager.type}") + private String type; + + private Storage storage; + + @Autowired + LocalStorage local; + + @Autowired + MinioStorage minio; + + @Autowired + AliYunOSSStorage aliYunOSS; + + @Override + public void run(ApplicationArguments args) throws Exception { + initStorage(); + } + + private void initStorage() throws Exception { + if (local.getType().equals(type)) { + storage = local; + } else if (minio.getType().equals(type)) { + storage = minio; + } else if (aliYunOSS.getType().equals(type)) { + storage = aliYunOSS; + } else { + throw new Exception("unknown storage type: " + type); + } + storage.init(); + } + + public String getUrl(@NotNull Resource file, boolean download) throws Exception { + return storage.getUrl(file, download); + } + + public InputStream getStream(String relativePath) throws Exception { + return storage.getStream(relativePath); + } + + public InputStream getStreamByResource(Resource resource) throws Exception { + return storage.getStream(resource.getPath() + "/" + resource.getName()); + } + + public void store(Resource resource, MultipartFile file) throws Exception { + storage.store(resource, file); + } + + public void remove(Resource file) throws Exception { + storage.remove(file); + } + + public long getExpireTime() { + return storage.getExpireTime(); + } +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/Storage.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/Storage.java new file mode 100644 index 0000000..966bca8 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/Storage.java @@ -0,0 +1,23 @@ +package cn.crtech.cloud.resmanager.server; + +import cn.crtech.cloud.resmanager.pojo.Resource; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotNull; +import java.io.InputStream; + +public interface Storage { + String getType(); + + void init() throws Exception; + + String getUrl(@NotNull Resource file, boolean download) throws Exception; + + InputStream getStream(String relativePath) throws Exception; + + void store(Resource resource, MultipartFile file) throws Exception; + + void remove(Resource file) throws Exception; + + long getExpireTime(); +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/AliYunOSSStorage.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/AliYunOSSStorage.java new file mode 100644 index 0000000..ad492f2 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/AliYunOSSStorage.java @@ -0,0 +1,50 @@ +package cn.crtech.cloud.resmanager.server.impl; + +import cn.crtech.cloud.resmanager.config.AliYunOSSConfig; +import cn.crtech.cloud.resmanager.pojo.Resource; +import cn.crtech.cloud.resmanager.server.Storage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +@Component +public class AliYunOSSStorage implements Storage { + @Autowired + private AliYunOSSConfig config; + + @Override + public String getType() { + return "aliyunoss"; + } + + @Override + public void init() throws Exception { + } + + @Override + public String getUrl(Resource file, boolean download) throws Exception { + return null; + } + + @Override + public InputStream getStream(String relativePath) throws Exception { + return null; + } + + @Override + public void store(Resource resource, MultipartFile file) throws Exception { + + } + + @Override + public void remove(Resource file) throws Exception { + + } + + @Override + public long getExpireTime() { + return 0; + } +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/LocalStorage.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/LocalStorage.java new file mode 100644 index 0000000..9c1b6bd --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/LocalStorage.java @@ -0,0 +1,100 @@ +package cn.crtech.cloud.resmanager.server.impl; + +import cn.crtech.cloud.resmanager.config.LocalConfig; +import cn.crtech.cloud.resmanager.pojo.Resource; +import cn.crtech.cloud.resmanager.server.Storage; +import cn.hutool.core.util.ArrayUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.URLEncoder; + +@Component +public class LocalStorage implements Storage { + @Autowired + private LocalConfig config; + + @Override + public String getType() { + return "local"; + } + + @Override + public void init() throws Exception { + File file = new File(config.getPath(), config.getBucketName()); + if (!file.exists()) { + file.mkdirs(); + } + } + + @Override + public String getUrl(Resource file, boolean download) throws Exception { + String[] split = file.getPath().split("/"); + for (int i = 0; i < split.length; i++) { + split[i] = URLEncoder.encode(split[i], "UTF-8"); + } + String path = "/" + ArrayUtil.join(split, "/") + "/" + URLEncoder.encode(file.getName(), "UTF-8"); + if (download) { + return "/down/" + file.getMimeType() + path; + } else { + return "/res/" + file.getMimeType() + path; + } + } + + @Override + public InputStream getStream(String relativePath) throws Exception { + File file = new File(config.getPath(), config.getBucketName()); + if (!file.exists()) { + file.mkdirs(); + } + file = new File(file, relativePath); + if (!file.exists()) { + throw new FileNotFoundException(file.getAbsolutePath()); + } + return new FileInputStream(file); + } + + @Override + public void store(Resource resource, MultipartFile file) throws Exception { + File f = new File(config.getPath(), config.getBucketName()); + if (!f.exists()) { + f.mkdirs(); + } + f = new File(f, resource.getPath().substring(1)); + if (!f.exists()) { + f.mkdirs(); + } + f = new File(f, resource.getName()); + if (f.exists()) { + throw new Exception("文件已存在"); + } + file.transferTo(f); + } + + @Override + public void remove(Resource file) throws Exception { + File f = new File(config.getPath(), config.getBucketName()); + if (!f.exists()) { + f.mkdirs(); + } + f = new File(f, file.getPath().substring(1)); + if (!f.exists()) { + f.mkdirs(); + } + f = new File(f, file.getName()); + if (f.exists()) { + f.delete(); + } + } + + @Override + public long getExpireTime() { + return 60 * 60; + } + +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/MinioStorage.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/MinioStorage.java new file mode 100644 index 0000000..279c9a2 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/server/impl/MinioStorage.java @@ -0,0 +1,118 @@ +package cn.crtech.cloud.resmanager.server.impl; + +import cn.crtech.cloud.resmanager.config.MinioConfig; +import cn.crtech.cloud.resmanager.pojo.Resource; +import cn.crtech.cloud.resmanager.server.Storage; +import cn.hutool.core.util.ArrayUtil; +import io.minio.MinioClient; +import io.minio.ObjectStat; +import io.minio.PutObjectOptions; +import io.minio.errors.ErrorResponseException; +import io.minio.http.Method; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +@Component +public class MinioStorage implements Storage { + @Autowired + private MinioConfig config; + + @Override + public String getType() { + return "minio"; + } + + private MinioClient minioClient; + + @Override + public void init() throws Exception { + if (minioClient == null) { + minioClient = new MinioClient(config.getEndpoint(), config.getPort(), config.getAccessKey(), config.getSecretKey(), config.getSecure()); + } + if (!minioClient.bucketExists(config.getBucketName())) { + minioClient.makeBucket(config.getBucketName()); + //设置桶权限 +// JSONObject jsonObject = new JSONObject(); +// ArrayList> object = new ArrayList>(); +// Map Map1 = new HashMap<>(); +// String[] str = new String[]{"s3:GetBucketLocation","s3:ListBucket"}; +// Map1.put("Action",str); +// Map1.put("Effect","Allow"); +// Map1.put("Principal","*"); +// Map1.put("Resource","arn:aws:s3:::"+config.getBucketName()); +// object.add(Map1); +// Map Map2 = new HashMap<>(); +// Map2.put("Action","s3:GetObject"); +// Map2.put("Effect","Allow"); +// Map2.put("Principal","*"); +// Map2.put("Resource","arn:aws:s3:::"+config.getBucketName()+"/myobject*"); +// object.add(Map2); +// jsonObject.set("Statement",object); +// jsonObject.set("Version","2012-10-17"); +// String policyJson = jsonObject.toString(); +// minioClient.setBucketPolicy(config.getBucketName(),policyJson); + } + } + + @Override + public String getUrl(Resource file, boolean download) throws Exception { + String[] split = file.getPath().split("/"); + for (int i = 0; i < split.length; i++) { + split[i] = URLEncoder.encode(split[i], "UTF-8"); + } + String path = "/" + ArrayUtil.join(split, "/") + "/" + URLEncoder.encode(file.getName(), "UTF-8"); + if ("minio".equals(config.getAccessType())) { + Map queryParams = new HashMap<>(); + if (download) { + queryParams.put("response-content-disposition", "attachment;"); + } + return minioClient.getPresignedObjectUrl(Method.GET, config.getBucketName(), file.getPath().substring(1) + "/" + file.getName(), config.getUrlExpires(), queryParams); + } else if (download) { + return "/down/" + file.getMimeType() + path; + } else { + return "/res/" + file.getMimeType() + path; + } + } + + @Override + public InputStream getStream(String relativePath) throws Exception { + return minioClient.getObject(config.getBucketName(), relativePath.substring(1)); + } + + @Override + public void store(Resource resource, MultipartFile file) throws Exception { + try { + ObjectStat objectStat = minioClient.statObject(config.getBucketName(), resource.getPath() + "/" + resource.getName()); + if (objectStat != null) { + throw new Exception("文件已存在"); + } + PutObjectOptions options = new PutObjectOptions(resource.getSize(), 5L * 1024 * 1024); //5M分片存储 + options.setContentType(resource.getMimeType()); + minioClient.putObject(config.getBucketName(), resource.getPath() + "/" + resource.getName(), file.getInputStream(), options); + } catch (ErrorResponseException e) { + if ("Object does not exist".equals(e.getMessage())) { + PutObjectOptions options = new PutObjectOptions(resource.getSize(), 5L * 1024 * 1024); //5M分片存储 + options.setContentType(resource.getMimeType()); + minioClient.putObject(config.getBucketName(), resource.getPath() + "/" + resource.getName(), file.getInputStream(), options); + } else { + throw new Exception(e); + } + } + } + + @Override + public void remove(Resource file) throws Exception { + minioClient.removeObject(config.getBucketName(), file.getPath() + "/" + file.getName()); + } + + @Override + public long getExpireTime() { + return config.getUrlExpires(); + } +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/service/BaseService.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/service/BaseService.java new file mode 100644 index 0000000..e6d7140 --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/service/BaseService.java @@ -0,0 +1,48 @@ +package cn.crtech.cloud.resmanager.service; + +import cn.crtech.cloud.common.dto.Result; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Map; + +/** + * Created by Onion on 2022/01/11. + */ +public abstract class BaseService { + public abstract Mapper getMapper(); + + @Transactional(propagation = Propagation.REQUIRED) + public Result add(T obj) { + getMapper().insert(obj); + return Result.success(obj, "操作成功!"); + } + + @Transactional(propagation = Propagation.REQUIRED) + public Result update(T obj) { + getMapper().updateByPrimaryKeySelective(obj); + return Result.success(obj, "操作成功!"); + } + + @Transactional(propagation = Propagation.REQUIRED) + public Result deleteById(int id) { + getMapper().deleteByPrimaryKey(id); + return Result.success(id, "操作成功!"); + } + + @Transactional(propagation = Propagation.REQUIRED) + public Result delete(T obj) { + getMapper().delete(obj); + return Result.success(obj, "操作成功!"); + } + + public T getById(int id) { + return (T) getMapper().selectByPrimaryKey(id); + } + + public abstract Result listByPage(Map params); + + public abstract Result listByParams(Map params); + +} diff --git a/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/service/ResourceService.java b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/service/ResourceService.java new file mode 100644 index 0000000..e4f267b --- /dev/null +++ b/ResourceManager/src/main/java/cn/crtech/cloud/resmanager/service/ResourceService.java @@ -0,0 +1,203 @@ +package cn.crtech.cloud.resmanager.service; + +import cn.crtech.cloud.common.dto.Result; +import cn.crtech.cloud.common.utils.CommonFun; +import cn.crtech.cloud.resmanager.mapper.ResourceMapper; +import cn.crtech.cloud.resmanager.pojo.Resource; +import cn.crtech.cloud.resmanager.server.ResmanagerServer; +import cn.hutool.core.util.StrUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import tk.mybatis.mapper.common.Mapper; + +import javax.validation.constraints.NotNull; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Service("resService") +public class ResourceService extends BaseService { + public static final String RESOURCE_URL_KEY = "RM:URL:"; + public static final String RESOURCE_DOWNLOAD_KEY = "RM:DOWNLOAD:"; + + @Autowired + private ResourceMapper resourceMapper; + + @Override + public Mapper getMapper() { + return resourceMapper; + } + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private ResmanagerServer server; + + public Resource get(String fileId) { + return resourceMapper.selectByPrimaryKey(fileId); + } + + public List listByIds(List fileIds) { + return resourceMapper.selectByIdList(fileIds); + } + + public int remove(List fileIds) throws Exception { + for (String fileId : fileIds) { + Resource resource = get(fileId); + if (resource.getIsTemp() == 1 && resource.getTempOper() == 1) { + server.remove(resource); + resourceMapper.deleteByPrimaryKey(fileId); + } else { + resource.setIsTemp(1); + resource.setTempOper(3); + resourceMapper.updateByPrimaryKeySelective(resource); + } + } + return fileIds.size(); + } + + public int commit(List fileIds) throws Exception { + for (String fileId : fileIds) { + Resource resource = get(fileId); + if (resource.getIsTemp() == 1 && resource.getTempOper() == 3) { + server.remove(resource); + resourceMapper.deleteByPrimaryKey(fileId); + } else if (resource.getIsTemp() == 1) { + resource.setIsTemp(0); + resource.setTempOper(0); + resource.setLastModifyTime(Calendar.getInstance().getTime()); +// resource.setLastModifyUser(loginUserHolder.getCurrentUser().getId()); + resourceMapper.updateByPrimaryKeySelective(resource); + } + } + return fileIds.size(); + } + + public int rollback(List fileIds) throws Exception { + for (String fileId : fileIds) { + Resource resource = get(fileId); + if (resource.getIsTemp() == 1 && resource.getTempOper() == 1) { + server.remove(resource); + resourceMapper.deleteByPrimaryKey(fileId); + } else if (resource.getIsTemp() == 1) { + resource.setIsTemp(0); + resource.setTempOper(0); + resourceMapper.updateByPrimaryKeySelective(resource); + } + } + return fileIds.size(); + } + + public String getUrl(String fileId, boolean download) throws Exception { + String key = (download ? RESOURCE_DOWNLOAD_KEY : RESOURCE_URL_KEY) + fileId; + String url = (String) redisTemplate.opsForValue().get(key); + if (StrUtil.isNotBlank(url)) { + return url; + } + Resource resource = get(fileId); + url = server.getUrl(resource, download); + redisTemplate.opsForValue().set(key, url, server.getExpireTime(), TimeUnit.SECONDS); + return url; + } + + public List getMuchUrl(List list, boolean download) throws Exception { + List backList = new ArrayList<>(); + for (int i = 0; i <= (list.size() - 1); i++) { + String url = getUrl(list.get(i), download); + backList.add(url); + } + return backList; + } + + public InputStream getStream(String fileId) throws Exception { + Resource resource = get(fileId); + return server.getStream(resource.getPath()); + } + + public InputStream getStreamByFileId(String fileId) throws Exception { + return server.getStreamByResource(get(fileId)); + } + + public InputStream getStreamByPath(String relativePath) throws Exception { + return server.getStream(relativePath); + } + + public Resource addFile(@NotNull MultipartFile file, String path, String name, String description) throws Exception { + /* UserDto userDto = loginUserHolder.getCurrentUser();*/ + Resource resource = new Resource(); + resource.setId(CommonFun.getSSID() + ""); + resource.setMimeType(file.getContentType()); + resource.setName(StrUtil.isBlank(name) ? file.getOriginalFilename() : name); + resource.setSize(file.getSize()); + resource.setPath(StrUtil.isBlank(path) ? "/" : (path.startsWith("/") ? path : ("/" + path))); + resource.setDescription(description); + resource.setCreateTime(Calendar.getInstance().getTime()); +/* if (userDto != null) { + resource.setCreateUser(loginUserHolder.getCurrentUser().getId()); + resource.setLastModifyUser(loginUserHolder.getCurrentUser().getId()); + }*/ + resource.setLastModifyTime(Calendar.getInstance().getTime()); + + resource.setIsTemp(1); + resource.setTempOper(1); + + synchronized (this) { + server.store(resource, file); + } + + resourceMapper.insert(resource); + return resource; + } + + public Resource copyOfMinIoFile(String fileId) throws Exception { + Resource selectResource = get(fileId); + if (selectResource == null) { + return null; + } + MultipartFile multipartFile = new MockMultipartFile(selectResource.getName(), getStreamByFileId(fileId)); + String fileName = selectResource.getName(); + //创建新文件 + Resource resource = new Resource(); + resource.setId(CommonFun.getSSID() + ""); + resource.setMimeType(selectResource.getMimeType()); + resource.setName(CommonFun.getSSID() + "." + getFileSuffixName(selectResource.getName())); + resource.setSize(multipartFile.getSize()); + resource.setPath(selectResource.getPath()); + resource.setDescription("复制文件"); + resource.setCreateTime(Calendar.getInstance().getTime()); + resource.setLastModifyTime(Calendar.getInstance().getTime()); + resource.setIsTemp(1); + resource.setTempOper(1); + synchronized (this) { + server.store(resource, multipartFile); + } + + resourceMapper.insert(resource); + return resource; + } + + public static String getFileSuffixName(String image_name) { + return image_name.contains(".") ? image_name.substring(image_name.lastIndexOf(".") + 1) : null; + } + + + @Override + public Result listByPage(Map params) { + return null; + } + + @Override + public Result listByParams(Map params) { + return null; + } + + +} + diff --git a/ResourceManager/src/main/resources/application-dev.yml b/ResourceManager/src/main/resources/application-dev.yml new file mode 100644 index 0000000..44ccd36 --- /dev/null +++ b/ResourceManager/src/main/resources/application-dev.yml @@ -0,0 +1,49 @@ +server: + port: 8082 + servlet: + encoding: + charset: utf-8 + enabled: true + force: true + tomcat: + uri-encoding: UTF-8 +logging: + config: classpath:logback.xml + file: + path: logs/crtech-cloud-resmanager.log + level: + cn.crtech.cloud.resmanager: debug +spring: + servlet: + multipart: + enabled: true + max-file-size: 100MB + max-request-size: 1024MB +mybatis: + mapper-locations: classpath:/mapping/*.xml + type-aliases-package: cn.crtech.cloud.resmanager.pojo + #配置驼峰下划线 + configuration: + map-underscore-to-camel-case: true +resmanager: + type: minio #local,minio,aliyunoss + local: + path: D:/minioserver + bucket-name: crtech-cloud + minio: + endpoint: 127.0.0.1 + port: 9000 + access-key: minioadmin + secret-key: minioadmin + bucket-name: crtech-cloud + secure: false + access-type: minio #minio 直接获取minio的url-expires秒的有效url,local 获取本服务转发地址 + url-expires: 1800 #minio直连url有效时间:1~604800 秒 + aliyunoss: + server-url: https:// + access-key: minioadmin + secret-key: minioadmin + bucket-name: crtech-cloud + secure: false + access-type: aliyunoss #aliyunoss 直接获取minio的url-expires秒的有效url,local 获取本服务转发地址 + url-expires: 1800 #minio直连url有效时间:1~604800 秒 diff --git a/ResourceManager/src/main/resources/application-prod.yml b/ResourceManager/src/main/resources/application-prod.yml new file mode 100644 index 0000000..a53fda0 --- /dev/null +++ b/ResourceManager/src/main/resources/application-prod.yml @@ -0,0 +1,49 @@ +server: + port: 8082 + servlet: + encoding: + charset: utf-8 + enabled: true + force: true + tomcat: + uri-encoding: UTF-8 +logging: + config: classpath:logback.xml + file: + path: logs/crtech-cloud-resmanager.log + level: + cn.crtech.cloud.resmanager: debug +spring: + servlet: + multipart: + enabled: true + max-file-size: 100MB + max-request-size: 1024MB +mybatis: + mapper-locations: classpath:/mapping/*.xml + type-aliases-package: cn.crtech.cloud.resmanager.pojo + #配置驼峰下划线 + configuration: + map-underscore-to-camel-case: true +resmanager: + type: minio #local,minio,aliyunoss + local: + path: D:/minioserver + bucket-name: crtech-cloud + minio: + endpoint: http://api.yaoxunk.com + port: 9008 + access-key: minioadmin + secret-key: minioadmin + bucket-name: crtech-cloud + secure: false + access-type: minio #minio 直接获取minio的url-expires秒的有效url,local 获取本服务转发地址 + url-expires: 1800 #minio直连url有效时间:1~604800 秒 + aliyunoss: + server-url: https:// + access-key: minioadmin + secret-key: minioadmin + bucket-name: crtech-cloud + secure: false + access-type: aliyunoss #aliyunoss 直接获取minio的url-expires秒的有效url,local 获取本服务转发地址 + url-expires: 1800 #minio直连url有效时间:1~604800 秒 diff --git a/ResourceManager/src/main/resources/bootstrap-dev.yml b/ResourceManager/src/main/resources/bootstrap-dev.yml new file mode 100644 index 0000000..8251662 --- /dev/null +++ b/ResourceManager/src/main/resources/bootstrap-dev.yml @@ -0,0 +1,16 @@ +spring: + application: + name: crtech-cloud-resmanager # 项目名称尽量用小写 + cloud: + nacos: + discovery: + server-addr: localhost:8848 + redis: + database: 0 + port: 6379 + host: localhost + password: + datasource: + url: jdbc:mysql://chaoran.crtech.cn:9803/cr_cloud_general_dev?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true + username: crtech + password: www.server41.com diff --git a/ResourceManager/src/main/resources/bootstrap-prod.yml b/ResourceManager/src/main/resources/bootstrap-prod.yml new file mode 100644 index 0000000..8bbe5fc --- /dev/null +++ b/ResourceManager/src/main/resources/bootstrap-prod.yml @@ -0,0 +1,16 @@ +spring: + application: + name: crtech-cloud-resmanager # 项目名称尽量用小写 + cloud: + nacos: + discovery: + server-addr: http://api.yaoxunk.com:28848 + redis: + database: 0 + port: 6689 + host: 127.0.0.1 + password: + datasource: + url: jdbc:mysql://rm-uf6wn1g5bm13lf3363o.mysql.rds.aliyuncs.com:3306/cr_cloud_general?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true + username: crtech + password: Crtech@yun_2023 \ No newline at end of file diff --git a/ResourceManager/src/main/resources/bootstrap.yml b/ResourceManager/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..1ab53e5 --- /dev/null +++ b/ResourceManager/src/main/resources/bootstrap.yml @@ -0,0 +1,6 @@ +spring: + profiles: + active: dev + + + diff --git a/ResourceManager/src/main/resources/logback.xml b/ResourceManager/src/main/resources/logback.xml new file mode 100644 index 0000000..93c2f47 --- /dev/null +++ b/ResourceManager/src/main/resources/logback.xml @@ -0,0 +1,56 @@ + + + + + + logs/crtech-cloud-resmanager.%d{yyyy-MM-dd}.log + + + + + %d{yyyy-MM-dd_HH:mm:ss} %logger{18} -%msg%n + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + 0 + 1000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ResourceManager/src/main/resources/public/sdk.js b/ResourceManager/src/main/resources/public/sdk.js new file mode 100644 index 0000000..2afd9b7 --- /dev/null +++ b/ResourceManager/src/main/resources/public/sdk.js @@ -0,0 +1,29 @@ +/** + * Crtech Cloud ResourceManager Javascript SDK File + */ + +var $rm = $rm || {}; +(function(rm, window) { + function getSrc() { + var scriptSrc = document.getElementsByTagName('script')[document.getElementsByTagName('script').length - 1].src; + return scriptSrc; + } + + rm.src = getSrc(); + rm.server_url = rm.src.substring(0, rm.src.indexOf('/sdk.js')); + + rm.show_url = rm.server_url + "/s/"; + rm.dowload_url = rm.server_url + "/d/"; + + rm.init = function() { + + } + + +})($rm, window); + +/** + * axios 0.24.0(October 25, 2021) + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=11)}([function(e,t,n){"use strict";var r=n(3),o=Object.prototype.toString;function i(e){return"[object Array]"===o.call(e)}function s(e){return void 0===e}function a(e){return null!==e&&"object"==typeof e}function u(e){if("[object Object]"!==o.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function c(e){return"[object Function]"===o.call(e)}function f(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),i(e))for(var n=0,r=e.length;n=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],(function(e){c.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){c.headers[e]=r.merge(s)})),e.exports=c},function(e,t,n){"use strict";function r(e){this.message=e}r.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},r.prototype.__CANCEL__=!0,e.exports=r},function(e,t,n){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r=0)return;s[t]="set-cookie"===t?(s[t]?s[t]:[]).concat([n]):s[t]?s[t]+", "+n:n}})),s):s}},function(e,t,n){"use strict";var r=n(0);e.exports=r.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function o(e){var r=e;return t&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return e=o(window.location.href),function(t){var n=r.isString(t)?o(t):t;return n.protocol===e.protocol&&n.host===e.host}}():function(){return!0}},function(e,t,n){"use strict";var r=n(10).version,o={};["object","boolean","number","function","string","symbol"].forEach((function(e,t){o[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}}));var i={};o.transitional=function(e,t,n){function o(e,t){return"[Axios v"+r+"] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,r,s){if(!1===e)throw new Error(o(r," has been removed"+(t?" in "+t:"")));return t&&!i[r]&&(i[r]=!0,console.warn(o(r," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,r,s)}},e.exports={assertOptions:function(e,t,n){if("object"!=typeof e)throw new TypeError("options must be an object");for(var r=Object.keys(e),o=r.length;o-- >0;){var i=r[o],s=t[i];if(s){var a=e[i],u=void 0===a||s(a,i,e);if(!0!==u)throw new TypeError("option "+i+" must be "+u)}else if(!0!==n)throw Error("Unknown option "+i)}},validators:o}},function(e,t,n){"use strict";var r=n(2);function o(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var n=this;this.promise.then((function(e){if(n._listeners){var t,r=n._listeners.length;for(t=0;t undefined\n * typeof document -> undefined\n *\n * react-native:\n * navigator.product -> 'ReactNative'\n * nativescript\n * navigator.product -> 'NativeScript' or 'NS'\n */\nfunction isStandardBrowserEnv() {\n if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||\n navigator.product === 'NativeScript' ||\n navigator.product === 'NS')) {\n return false;\n }\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined'\n );\n}\n\n/**\n * Iterate over an Array or an Object invoking a function for each item.\n *\n * If `obj` is an Array callback will be called passing\n * the value, index, and complete array for each item.\n *\n * If 'obj' is an Object callback will be called passing\n * the value, key, and complete object for each property.\n *\n * @param {Object|Array} obj The object to iterate\n * @param {Function} fn The callback to invoke for each item\n */\nfunction forEach(obj, fn) {\n // Don't bother if no value provided\n if (obj === null || typeof obj === 'undefined') {\n return;\n }\n\n // Force an array if not already something iterable\n if (typeof obj !== 'object') {\n /*eslint no-param-reassign:0*/\n obj = [obj];\n }\n\n if (isArray(obj)) {\n // Iterate over array values\n for (var i = 0, l = obj.length; i < l; i++) {\n fn.call(null, obj[i], i, obj);\n }\n } else {\n // Iterate over object keys\n for (var key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n fn.call(null, obj[key], key, obj);\n }\n }\n }\n}\n\n/**\n * Accepts varargs expecting each argument to be an object, then\n * immutably merges the properties of each object and returns result.\n *\n * When multiple objects contain the same key the later object in\n * the arguments list will take precedence.\n *\n * Example:\n *\n * ```js\n * var result = merge({foo: 123}, {foo: 456});\n * console.log(result.foo); // outputs 456\n * ```\n *\n * @param {Object} obj1 Object to merge\n * @returns {Object} Result of all merge properties\n */\nfunction merge(/* obj1, obj2, obj3, ... */) {\n var result = {};\n function assignValue(val, key) {\n if (isPlainObject(result[key]) && isPlainObject(val)) {\n result[key] = merge(result[key], val);\n } else if (isPlainObject(val)) {\n result[key] = merge({}, val);\n } else if (isArray(val)) {\n result[key] = val.slice();\n } else {\n result[key] = val;\n }\n }\n\n for (var i = 0, l = arguments.length; i < l; i++) {\n forEach(arguments[i], assignValue);\n }\n return result;\n}\n\n/**\n * Extends object a by mutably adding to it the properties of object b.\n *\n * @param {Object} a The object to be extended\n * @param {Object} b The object to copy properties from\n * @param {Object} thisArg The object to bind function to\n * @return {Object} The resulting value of object a\n */\nfunction extend(a, b, thisArg) {\n forEach(b, function assignValue(val, key) {\n if (thisArg && typeof val === 'function') {\n a[key] = bind(val, thisArg);\n } else {\n a[key] = val;\n }\n });\n return a;\n}\n\n/**\n * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)\n *\n * @param {string} content with BOM\n * @return {string} content value without BOM\n */\nfunction stripBOM(content) {\n if (content.charCodeAt(0) === 0xFEFF) {\n content = content.slice(1);\n }\n return content;\n}\n\nmodule.exports = {\n isArray: isArray,\n isArrayBuffer: isArrayBuffer,\n isBuffer: isBuffer,\n isFormData: isFormData,\n isArrayBufferView: isArrayBufferView,\n isString: isString,\n isNumber: isNumber,\n isObject: isObject,\n isPlainObject: isPlainObject,\n isUndefined: isUndefined,\n isDate: isDate,\n isFile: isFile,\n isBlob: isBlob,\n isFunction: isFunction,\n isStream: isStream,\n isURLSearchParams: isURLSearchParams,\n isStandardBrowserEnv: isStandardBrowserEnv,\n forEach: forEach,\n merge: merge,\n extend: extend,\n trim: trim,\n stripBOM: stripBOM\n};\n","'use strict';\n\nvar utils = require('./utils');\nvar normalizeHeaderName = require('./helpers/normalizeHeaderName');\nvar enhanceError = require('./core/enhanceError');\n\nvar DEFAULT_CONTENT_TYPE = {\n 'Content-Type': 'application/x-www-form-urlencoded'\n};\n\nfunction setContentTypeIfUnset(headers, value) {\n if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {\n headers['Content-Type'] = value;\n }\n}\n\nfunction getDefaultAdapter() {\n var adapter;\n if (typeof XMLHttpRequest !== 'undefined') {\n // For browsers use XHR adapter\n adapter = require('./adapters/xhr');\n } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {\n // For node use HTTP adapter\n adapter = require('./adapters/http');\n }\n return adapter;\n}\n\nfunction stringifySafely(rawValue, parser, encoder) {\n if (utils.isString(rawValue)) {\n try {\n (parser || JSON.parse)(rawValue);\n return utils.trim(rawValue);\n } catch (e) {\n if (e.name !== 'SyntaxError') {\n throw e;\n }\n }\n }\n\n return (encoder || JSON.stringify)(rawValue);\n}\n\nvar defaults = {\n\n transitional: {\n silentJSONParsing: true,\n forcedJSONParsing: true,\n clarifyTimeoutError: false\n },\n\n adapter: getDefaultAdapter(),\n\n transformRequest: [function transformRequest(data, headers) {\n normalizeHeaderName(headers, 'Accept');\n normalizeHeaderName(headers, 'Content-Type');\n\n if (utils.isFormData(data) ||\n utils.isArrayBuffer(data) ||\n utils.isBuffer(data) ||\n utils.isStream(data) ||\n utils.isFile(data) ||\n utils.isBlob(data)\n ) {\n return data;\n }\n if (utils.isArrayBufferView(data)) {\n return data.buffer;\n }\n if (utils.isURLSearchParams(data)) {\n setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');\n return data.toString();\n }\n if (utils.isObject(data) || (headers && headers['Content-Type'] === 'application/json')) {\n setContentTypeIfUnset(headers, 'application/json');\n return stringifySafely(data);\n }\n return data;\n }],\n\n transformResponse: [function transformResponse(data) {\n var transitional = this.transitional || defaults.transitional;\n var silentJSONParsing = transitional && transitional.silentJSONParsing;\n var forcedJSONParsing = transitional && transitional.forcedJSONParsing;\n var strictJSONParsing = !silentJSONParsing && this.responseType === 'json';\n\n if (strictJSONParsing || (forcedJSONParsing && utils.isString(data) && data.length)) {\n try {\n return JSON.parse(data);\n } catch (e) {\n if (strictJSONParsing) {\n if (e.name === 'SyntaxError') {\n throw enhanceError(e, this, 'E_JSON_PARSE');\n }\n throw e;\n }\n }\n }\n\n return data;\n }],\n\n /**\n * A timeout in milliseconds to abort a request. If set to 0 (default) a\n * timeout is not created.\n */\n timeout: 0,\n\n xsrfCookieName: 'XSRF-TOKEN',\n xsrfHeaderName: 'X-XSRF-TOKEN',\n\n maxContentLength: -1,\n maxBodyLength: -1,\n\n validateStatus: function validateStatus(status) {\n return status >= 200 && status < 300;\n },\n\n headers: {\n common: {\n 'Accept': 'application/json, text/plain, */*'\n }\n }\n};\n\nutils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {\n defaults.headers[method] = {};\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);\n});\n\nmodule.exports = defaults;\n","'use strict';\n\n/**\n * A `Cancel` is an object that is thrown when an operation is canceled.\n *\n * @class\n * @param {string=} message The message.\n */\nfunction Cancel(message) {\n this.message = message;\n}\n\nCancel.prototype.toString = function toString() {\n return 'Cancel' + (this.message ? ': ' + this.message : '');\n};\n\nCancel.prototype.__CANCEL__ = true;\n\nmodule.exports = Cancel;\n","'use strict';\n\nmodule.exports = function bind(fn, thisArg) {\n return function wrap() {\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i];\n }\n return fn.apply(thisArg, args);\n };\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction encode(val) {\n return encodeURIComponent(val).\n replace(/%3A/gi, ':').\n replace(/%24/g, '$').\n replace(/%2C/gi, ',').\n replace(/%20/g, '+').\n replace(/%5B/gi, '[').\n replace(/%5D/gi, ']');\n}\n\n/**\n * Build a URL by appending params to the end\n *\n * @param {string} url The base of the url (e.g., http://www.google.com)\n * @param {object} [params] The params to be appended\n * @returns {string} The formatted url\n */\nmodule.exports = function buildURL(url, params, paramsSerializer) {\n /*eslint no-param-reassign:0*/\n if (!params) {\n return url;\n }\n\n var serializedParams;\n if (paramsSerializer) {\n serializedParams = paramsSerializer(params);\n } else if (utils.isURLSearchParams(params)) {\n serializedParams = params.toString();\n } else {\n var parts = [];\n\n utils.forEach(params, function serialize(val, key) {\n if (val === null || typeof val === 'undefined') {\n return;\n }\n\n if (utils.isArray(val)) {\n key = key + '[]';\n } else {\n val = [val];\n }\n\n utils.forEach(val, function parseValue(v) {\n if (utils.isDate(v)) {\n v = v.toISOString();\n } else if (utils.isObject(v)) {\n v = JSON.stringify(v);\n }\n parts.push(encode(key) + '=' + encode(v));\n });\n });\n\n serializedParams = parts.join('&');\n }\n\n if (serializedParams) {\n var hashmarkIndex = url.indexOf('#');\n if (hashmarkIndex !== -1) {\n url = url.slice(0, hashmarkIndex);\n }\n\n url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n }\n\n return url;\n};\n","'use strict';\n\n/**\n * Update an Error with the specified config, error code, and response.\n *\n * @param {Error} error The error to update.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The error.\n */\nmodule.exports = function enhanceError(error, config, code, request, response) {\n error.config = config;\n if (code) {\n error.code = code;\n }\n\n error.request = request;\n error.response = response;\n error.isAxiosError = true;\n\n error.toJSON = function toJSON() {\n return {\n // Standard\n message: this.message,\n name: this.name,\n // Microsoft\n description: this.description,\n number: this.number,\n // Mozilla\n fileName: this.fileName,\n lineNumber: this.lineNumber,\n columnNumber: this.columnNumber,\n stack: this.stack,\n // Axios\n config: this.config,\n code: this.code,\n status: this.response && this.response.status ? this.response.status : null\n };\n };\n return error;\n};\n","'use strict';\n\nvar utils = require('./../utils');\nvar settle = require('./../core/settle');\nvar cookies = require('./../helpers/cookies');\nvar buildURL = require('./../helpers/buildURL');\nvar buildFullPath = require('../core/buildFullPath');\nvar parseHeaders = require('./../helpers/parseHeaders');\nvar isURLSameOrigin = require('./../helpers/isURLSameOrigin');\nvar createError = require('../core/createError');\nvar defaults = require('../defaults');\nvar Cancel = require('../cancel/Cancel');\n\nmodule.exports = function xhrAdapter(config) {\n return new Promise(function dispatchXhrRequest(resolve, reject) {\n var requestData = config.data;\n var requestHeaders = config.headers;\n var responseType = config.responseType;\n var onCanceled;\n function done() {\n if (config.cancelToken) {\n config.cancelToken.unsubscribe(onCanceled);\n }\n\n if (config.signal) {\n config.signal.removeEventListener('abort', onCanceled);\n }\n }\n\n if (utils.isFormData(requestData)) {\n delete requestHeaders['Content-Type']; // Let the browser set it\n }\n\n var request = new XMLHttpRequest();\n\n // HTTP basic authentication\n if (config.auth) {\n var username = config.auth.username || '';\n var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';\n requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);\n }\n\n var fullPath = buildFullPath(config.baseURL, config.url);\n request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);\n\n // Set the request timeout in MS\n request.timeout = config.timeout;\n\n function onloadend() {\n if (!request) {\n return;\n }\n // Prepare the response\n var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;\n var responseData = !responseType || responseType === 'text' || responseType === 'json' ?\n request.responseText : request.response;\n var response = {\n data: responseData,\n status: request.status,\n statusText: request.statusText,\n headers: responseHeaders,\n config: config,\n request: request\n };\n\n settle(function _resolve(value) {\n resolve(value);\n done();\n }, function _reject(err) {\n reject(err);\n done();\n }, response);\n\n // Clean up request\n request = null;\n }\n\n if ('onloadend' in request) {\n // Use onloadend if available\n request.onloadend = onloadend;\n } else {\n // Listen for ready state to emulate onloadend\n request.onreadystatechange = function handleLoad() {\n if (!request || request.readyState !== 4) {\n return;\n }\n\n // The request errored out and we didn't get a response, this will be\n // handled by onerror instead\n // With one exception: request that using file: protocol, most browsers\n // will return status as 0 even though it's a successful request\n if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {\n return;\n }\n // readystate handler is calling before onerror or ontimeout handlers,\n // so we should call onloadend on the next 'tick'\n setTimeout(onloadend);\n };\n }\n\n // Handle browser request cancellation (as opposed to a manual cancellation)\n request.onabort = function handleAbort() {\n if (!request) {\n return;\n }\n\n reject(createError('Request aborted', config, 'ECONNABORTED', request));\n\n // Clean up request\n request = null;\n };\n\n // Handle low level network errors\n request.onerror = function handleError() {\n // Real errors are hidden from us by the browser\n // onerror should only fire if it's a network error\n reject(createError('Network Error', config, null, request));\n\n // Clean up request\n request = null;\n };\n\n // Handle timeout\n request.ontimeout = function handleTimeout() {\n var timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';\n var transitional = config.transitional || defaults.transitional;\n if (config.timeoutErrorMessage) {\n timeoutErrorMessage = config.timeoutErrorMessage;\n }\n reject(createError(\n timeoutErrorMessage,\n config,\n transitional.clarifyTimeoutError ? 'ETIMEDOUT' : 'ECONNABORTED',\n request));\n\n // Clean up request\n request = null;\n };\n\n // Add xsrf header\n // This is only done if running in a standard browser environment.\n // Specifically not if we're in a web worker, or react-native.\n if (utils.isStandardBrowserEnv()) {\n // Add xsrf header\n var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?\n cookies.read(config.xsrfCookieName) :\n undefined;\n\n if (xsrfValue) {\n requestHeaders[config.xsrfHeaderName] = xsrfValue;\n }\n }\n\n // Add headers to the request\n if ('setRequestHeader' in request) {\n utils.forEach(requestHeaders, function setRequestHeader(val, key) {\n if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {\n // Remove Content-Type if data is undefined\n delete requestHeaders[key];\n } else {\n // Otherwise add header to the request\n request.setRequestHeader(key, val);\n }\n });\n }\n\n // Add withCredentials to request if needed\n if (!utils.isUndefined(config.withCredentials)) {\n request.withCredentials = !!config.withCredentials;\n }\n\n // Add responseType to request if needed\n if (responseType && responseType !== 'json') {\n request.responseType = config.responseType;\n }\n\n // Handle progress if needed\n if (typeof config.onDownloadProgress === 'function') {\n request.addEventListener('progress', config.onDownloadProgress);\n }\n\n // Not all browsers support upload events\n if (typeof config.onUploadProgress === 'function' && request.upload) {\n request.upload.addEventListener('progress', config.onUploadProgress);\n }\n\n if (config.cancelToken || config.signal) {\n // Handle cancellation\n // eslint-disable-next-line func-names\n onCanceled = function(cancel) {\n if (!request) {\n return;\n }\n reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);\n request.abort();\n request = null;\n };\n\n config.cancelToken && config.cancelToken.subscribe(onCanceled);\n if (config.signal) {\n config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);\n }\n }\n\n if (!requestData) {\n requestData = null;\n }\n\n // Send the request\n request.send(requestData);\n });\n};\n","'use strict';\n\nvar enhanceError = require('./enhanceError');\n\n/**\n * Create an Error with the specified message, config, error code, request and response.\n *\n * @param {string} message The error message.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The created error.\n */\nmodule.exports = function createError(message, config, code, request, response) {\n var error = new Error(message);\n return enhanceError(error, config, code, request, response);\n};\n","'use strict';\n\nmodule.exports = function isCancel(value) {\n return !!(value && value.__CANCEL__);\n};\n","'use strict';\n\nvar utils = require('../utils');\n\n/**\n * Config-specific merge-function which creates a new config-object\n * by merging two configuration objects together.\n *\n * @param {Object} config1\n * @param {Object} config2\n * @returns {Object} New object resulting from merging config2 to config1\n */\nmodule.exports = function mergeConfig(config1, config2) {\n // eslint-disable-next-line no-param-reassign\n config2 = config2 || {};\n var config = {};\n\n function getMergedValue(target, source) {\n if (utils.isPlainObject(target) && utils.isPlainObject(source)) {\n return utils.merge(target, source);\n } else if (utils.isPlainObject(source)) {\n return utils.merge({}, source);\n } else if (utils.isArray(source)) {\n return source.slice();\n }\n return source;\n }\n\n // eslint-disable-next-line consistent-return\n function mergeDeepProperties(prop) {\n if (!utils.isUndefined(config2[prop])) {\n return getMergedValue(config1[prop], config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n return getMergedValue(undefined, config1[prop]);\n }\n }\n\n // eslint-disable-next-line consistent-return\n function valueFromConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n return getMergedValue(undefined, config2[prop]);\n }\n }\n\n // eslint-disable-next-line consistent-return\n function defaultToConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n return getMergedValue(undefined, config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n return getMergedValue(undefined, config1[prop]);\n }\n }\n\n // eslint-disable-next-line consistent-return\n function mergeDirectKeys(prop) {\n if (prop in config2) {\n return getMergedValue(config1[prop], config2[prop]);\n } else if (prop in config1) {\n return getMergedValue(undefined, config1[prop]);\n }\n }\n\n var mergeMap = {\n 'url': valueFromConfig2,\n 'method': valueFromConfig2,\n 'data': valueFromConfig2,\n 'baseURL': defaultToConfig2,\n 'transformRequest': defaultToConfig2,\n 'transformResponse': defaultToConfig2,\n 'paramsSerializer': defaultToConfig2,\n 'timeout': defaultToConfig2,\n 'timeoutMessage': defaultToConfig2,\n 'withCredentials': defaultToConfig2,\n 'adapter': defaultToConfig2,\n 'responseType': defaultToConfig2,\n 'xsrfCookieName': defaultToConfig2,\n 'xsrfHeaderName': defaultToConfig2,\n 'onUploadProgress': defaultToConfig2,\n 'onDownloadProgress': defaultToConfig2,\n 'decompress': defaultToConfig2,\n 'maxContentLength': defaultToConfig2,\n 'maxBodyLength': defaultToConfig2,\n 'transport': defaultToConfig2,\n 'httpAgent': defaultToConfig2,\n 'httpsAgent': defaultToConfig2,\n 'cancelToken': defaultToConfig2,\n 'socketPath': defaultToConfig2,\n 'responseEncoding': defaultToConfig2,\n 'validateStatus': mergeDirectKeys\n };\n\n utils.forEach(Object.keys(config1).concat(Object.keys(config2)), function computeConfigValue(prop) {\n var merge = mergeMap[prop] || mergeDeepProperties;\n var configValue = merge(prop);\n (utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue);\n });\n\n return config;\n};\n","module.exports = {\n \"version\": \"0.24.0\"\n};","module.exports = require('./lib/axios');","'use strict';\n\nvar utils = require('./utils');\nvar bind = require('./helpers/bind');\nvar Axios = require('./core/Axios');\nvar mergeConfig = require('./core/mergeConfig');\nvar defaults = require('./defaults');\n\n/**\n * Create an instance of Axios\n *\n * @param {Object} defaultConfig The default config for the instance\n * @return {Axios} A new instance of Axios\n */\nfunction createInstance(defaultConfig) {\n var context = new Axios(defaultConfig);\n var instance = bind(Axios.prototype.request, context);\n\n // Copy axios.prototype to instance\n utils.extend(instance, Axios.prototype, context);\n\n // Copy context to instance\n utils.extend(instance, context);\n\n // Factory for creating new instances\n instance.create = function create(instanceConfig) {\n return createInstance(mergeConfig(defaultConfig, instanceConfig));\n };\n\n return instance;\n}\n\n// Create the default instance to be exported\nvar axios = createInstance(defaults);\n\n// Expose Axios class to allow class inheritance\naxios.Axios = Axios;\n\n// Expose Cancel & CancelToken\naxios.Cancel = require('./cancel/Cancel');\naxios.CancelToken = require('./cancel/CancelToken');\naxios.isCancel = require('./cancel/isCancel');\naxios.VERSION = require('./env/data').version;\n\n// Expose all/spread\naxios.all = function all(promises) {\n return Promise.all(promises);\n};\naxios.spread = require('./helpers/spread');\n\n// Expose isAxiosError\naxios.isAxiosError = require('./helpers/isAxiosError');\n\nmodule.exports = axios;\n\n// Allow use of default import syntax in TypeScript\nmodule.exports.default = axios;\n","'use strict';\n\nvar utils = require('./../utils');\nvar buildURL = require('../helpers/buildURL');\nvar InterceptorManager = require('./InterceptorManager');\nvar dispatchRequest = require('./dispatchRequest');\nvar mergeConfig = require('./mergeConfig');\nvar validator = require('../helpers/validator');\n\nvar validators = validator.validators;\n/**\n * Create a new instance of Axios\n *\n * @param {Object} instanceConfig The default config for the instance\n */\nfunction Axios(instanceConfig) {\n this.defaults = instanceConfig;\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager()\n };\n}\n\n/**\n * Dispatch a request\n *\n * @param {Object} config The config specific for this request (merged with this.defaults)\n */\nAxios.prototype.request = function request(config) {\n /*eslint no-param-reassign:0*/\n // Allow for axios('example/url'[, config]) a la fetch API\n if (typeof config === 'string') {\n config = arguments[1] || {};\n config.url = arguments[0];\n } else {\n config = config || {};\n }\n\n config = mergeConfig(this.defaults, config);\n\n // Set config.method\n if (config.method) {\n config.method = config.method.toLowerCase();\n } else if (this.defaults.method) {\n config.method = this.defaults.method.toLowerCase();\n } else {\n config.method = 'get';\n }\n\n var transitional = config.transitional;\n\n if (transitional !== undefined) {\n validator.assertOptions(transitional, {\n silentJSONParsing: validators.transitional(validators.boolean),\n forcedJSONParsing: validators.transitional(validators.boolean),\n clarifyTimeoutError: validators.transitional(validators.boolean)\n }, false);\n }\n\n // filter out skipped interceptors\n var requestInterceptorChain = [];\n var synchronousRequestInterceptors = true;\n this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {\n if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {\n return;\n }\n\n synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;\n\n requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);\n });\n\n var responseInterceptorChain = [];\n this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {\n responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);\n });\n\n var promise;\n\n if (!synchronousRequestInterceptors) {\n var chain = [dispatchRequest, undefined];\n\n Array.prototype.unshift.apply(chain, requestInterceptorChain);\n chain = chain.concat(responseInterceptorChain);\n\n promise = Promise.resolve(config);\n while (chain.length) {\n promise = promise.then(chain.shift(), chain.shift());\n }\n\n return promise;\n }\n\n\n var newConfig = config;\n while (requestInterceptorChain.length) {\n var onFulfilled = requestInterceptorChain.shift();\n var onRejected = requestInterceptorChain.shift();\n try {\n newConfig = onFulfilled(newConfig);\n } catch (error) {\n onRejected(error);\n break;\n }\n }\n\n try {\n promise = dispatchRequest(newConfig);\n } catch (error) {\n return Promise.reject(error);\n }\n\n while (responseInterceptorChain.length) {\n promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());\n }\n\n return promise;\n};\n\nAxios.prototype.getUri = function getUri(config) {\n config = mergeConfig(this.defaults, config);\n return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\\?/, '');\n};\n\n// Provide aliases for supported request methods\nutils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, config) {\n return this.request(mergeConfig(config || {}, {\n method: method,\n url: url,\n data: (config || {}).data\n }));\n };\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, data, config) {\n return this.request(mergeConfig(config || {}, {\n method: method,\n url: url,\n data: data\n }));\n };\n});\n\nmodule.exports = Axios;\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction InterceptorManager() {\n this.handlers = [];\n}\n\n/**\n * Add a new interceptor to the stack\n *\n * @param {Function} fulfilled The function to handle `then` for a `Promise`\n * @param {Function} rejected The function to handle `reject` for a `Promise`\n *\n * @return {Number} An ID used to remove interceptor later\n */\nInterceptorManager.prototype.use = function use(fulfilled, rejected, options) {\n this.handlers.push({\n fulfilled: fulfilled,\n rejected: rejected,\n synchronous: options ? options.synchronous : false,\n runWhen: options ? options.runWhen : null\n });\n return this.handlers.length - 1;\n};\n\n/**\n * Remove an interceptor from the stack\n *\n * @param {Number} id The ID that was returned by `use`\n */\nInterceptorManager.prototype.eject = function eject(id) {\n if (this.handlers[id]) {\n this.handlers[id] = null;\n }\n};\n\n/**\n * Iterate over all the registered interceptors\n *\n * This method is particularly useful for skipping over any\n * interceptors that may have become `null` calling `eject`.\n *\n * @param {Function} fn The function to call for each interceptor\n */\nInterceptorManager.prototype.forEach = function forEach(fn) {\n utils.forEach(this.handlers, function forEachHandler(h) {\n if (h !== null) {\n fn(h);\n }\n });\n};\n\nmodule.exports = InterceptorManager;\n","'use strict';\n\nvar utils = require('./../utils');\nvar transformData = require('./transformData');\nvar isCancel = require('../cancel/isCancel');\nvar defaults = require('../defaults');\nvar Cancel = require('../cancel/Cancel');\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nfunction throwIfCancellationRequested(config) {\n if (config.cancelToken) {\n config.cancelToken.throwIfRequested();\n }\n\n if (config.signal && config.signal.aborted) {\n throw new Cancel('canceled');\n }\n}\n\n/**\n * Dispatch a request to the server using the configured adapter.\n *\n * @param {object} config The config that is to be used for the request\n * @returns {Promise} The Promise to be fulfilled\n */\nmodule.exports = function dispatchRequest(config) {\n throwIfCancellationRequested(config);\n\n // Ensure headers exist\n config.headers = config.headers || {};\n\n // Transform request data\n config.data = transformData.call(\n config,\n config.data,\n config.headers,\n config.transformRequest\n );\n\n // Flatten headers\n config.headers = utils.merge(\n config.headers.common || {},\n config.headers[config.method] || {},\n config.headers\n );\n\n utils.forEach(\n ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],\n function cleanHeaderConfig(method) {\n delete config.headers[method];\n }\n );\n\n var adapter = config.adapter || defaults.adapter;\n\n return adapter(config).then(function onAdapterResolution(response) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n response.data = transformData.call(\n config,\n response.data,\n response.headers,\n config.transformResponse\n );\n\n return response;\n }, function onAdapterRejection(reason) {\n if (!isCancel(reason)) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n if (reason && reason.response) {\n reason.response.data = transformData.call(\n config,\n reason.response.data,\n reason.response.headers,\n config.transformResponse\n );\n }\n }\n\n return Promise.reject(reason);\n });\n};\n","'use strict';\n\nvar utils = require('./../utils');\nvar defaults = require('./../defaults');\n\n/**\n * Transform the data for a request or a response\n *\n * @param {Object|String} data The data to be transformed\n * @param {Array} headers The headers for the request or response\n * @param {Array|Function} fns A single function or Array of functions\n * @returns {*} The resulting transformed data\n */\nmodule.exports = function transformData(data, headers, fns) {\n var context = this || defaults;\n /*eslint no-param-reassign:0*/\n utils.forEach(fns, function transform(fn) {\n data = fn.call(context, data, headers);\n });\n\n return data;\n};\n","'use strict';\n\nvar utils = require('../utils');\n\nmodule.exports = function normalizeHeaderName(headers, normalizedName) {\n utils.forEach(headers, function processHeader(value, name) {\n if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {\n headers[normalizedName] = value;\n delete headers[name];\n }\n });\n};\n","'use strict';\n\nvar createError = require('./createError');\n\n/**\n * Resolve or reject a Promise based on response status.\n *\n * @param {Function} resolve A function that resolves the promise.\n * @param {Function} reject A function that rejects the promise.\n * @param {object} response The response.\n */\nmodule.exports = function settle(resolve, reject, response) {\n var validateStatus = response.config.validateStatus;\n if (!response.status || !validateStatus || validateStatus(response.status)) {\n resolve(response);\n } else {\n reject(createError(\n 'Request failed with status code ' + response.status,\n response.config,\n null,\n response.request,\n response\n ));\n }\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs support document.cookie\n (function standardBrowserEnv() {\n return {\n write: function write(name, value, expires, path, domain, secure) {\n var cookie = [];\n cookie.push(name + '=' + encodeURIComponent(value));\n\n if (utils.isNumber(expires)) {\n cookie.push('expires=' + new Date(expires).toGMTString());\n }\n\n if (utils.isString(path)) {\n cookie.push('path=' + path);\n }\n\n if (utils.isString(domain)) {\n cookie.push('domain=' + domain);\n }\n\n if (secure === true) {\n cookie.push('secure');\n }\n\n document.cookie = cookie.join('; ');\n },\n\n read: function read(name) {\n var match = document.cookie.match(new RegExp('(^|;\\\\s*)(' + name + ')=([^;]*)'));\n return (match ? decodeURIComponent(match[3]) : null);\n },\n\n remove: function remove(name) {\n this.write(name, '', Date.now() - 86400000);\n }\n };\n })() :\n\n // Non standard browser env (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return {\n write: function write() {},\n read: function read() { return null; },\n remove: function remove() {}\n };\n })()\n);\n","'use strict';\n\nvar isAbsoluteURL = require('../helpers/isAbsoluteURL');\nvar combineURLs = require('../helpers/combineURLs');\n\n/**\n * Creates a new URL by combining the baseURL with the requestedURL,\n * only when the requestedURL is not already an absolute URL.\n * If the requestURL is absolute, this function returns the requestedURL untouched.\n *\n * @param {string} baseURL The base URL\n * @param {string} requestedURL Absolute or relative URL to combine\n * @returns {string} The combined full path\n */\nmodule.exports = function buildFullPath(baseURL, requestedURL) {\n if (baseURL && !isAbsoluteURL(requestedURL)) {\n return combineURLs(baseURL, requestedURL);\n }\n return requestedURL;\n};\n","'use strict';\n\n/**\n * Determines whether the specified URL is absolute\n *\n * @param {string} url The URL to test\n * @returns {boolean} True if the specified URL is absolute, otherwise false\n */\nmodule.exports = function isAbsoluteURL(url) {\n // A URL is considered absolute if it begins with \"://\" or \"//\" (protocol-relative URL).\n // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed\n // by any combination of letters, digits, plus, period, or hyphen.\n return /^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(url);\n};\n","'use strict';\n\n/**\n * Creates a new URL by combining the specified URLs\n *\n * @param {string} baseURL The base URL\n * @param {string} relativeURL The relative URL\n * @returns {string} The combined URL\n */\nmodule.exports = function combineURLs(baseURL, relativeURL) {\n return relativeURL\n ? baseURL.replace(/\\/+$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\n// Headers whose duplicates are ignored by node\n// c.f. https://nodejs.org/api/http.html#http_message_headers\nvar ignoreDuplicateOf = [\n 'age', 'authorization', 'content-length', 'content-type', 'etag',\n 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',\n 'last-modified', 'location', 'max-forwards', 'proxy-authorization',\n 'referer', 'retry-after', 'user-agent'\n];\n\n/**\n * Parse headers into an object\n *\n * ```\n * Date: Wed, 27 Aug 2014 08:58:49 GMT\n * Content-Type: application/json\n * Connection: keep-alive\n * Transfer-Encoding: chunked\n * ```\n *\n * @param {String} headers Headers needing to be parsed\n * @returns {Object} Headers parsed into an object\n */\nmodule.exports = function parseHeaders(headers) {\n var parsed = {};\n var key;\n var val;\n var i;\n\n if (!headers) { return parsed; }\n\n utils.forEach(headers.split('\\n'), function parser(line) {\n i = line.indexOf(':');\n key = utils.trim(line.substr(0, i)).toLowerCase();\n val = utils.trim(line.substr(i + 1));\n\n if (key) {\n if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {\n return;\n }\n if (key === 'set-cookie') {\n parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);\n } else {\n parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n }\n }\n });\n\n return parsed;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs have full support of the APIs needed to test\n // whether the request URL is of the same origin as current location.\n (function standardBrowserEnv() {\n var msie = /(msie|trident)/i.test(navigator.userAgent);\n var urlParsingNode = document.createElement('a');\n var originURL;\n\n /**\n * Parse a URL to discover it's components\n *\n * @param {String} url The URL to be parsed\n * @returns {Object}\n */\n function resolveURL(url) {\n var href = url;\n\n if (msie) {\n // IE needs attribute set twice to normalize properties\n urlParsingNode.setAttribute('href', href);\n href = urlParsingNode.href;\n }\n\n urlParsingNode.setAttribute('href', href);\n\n // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n return {\n href: urlParsingNode.href,\n protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n host: urlParsingNode.host,\n search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n hostname: urlParsingNode.hostname,\n port: urlParsingNode.port,\n pathname: (urlParsingNode.pathname.charAt(0) === '/') ?\n urlParsingNode.pathname :\n '/' + urlParsingNode.pathname\n };\n }\n\n originURL = resolveURL(window.location.href);\n\n /**\n * Determine if a URL shares the same origin as the current location\n *\n * @param {String} requestURL The URL to test\n * @returns {boolean} True if URL shares the same origin, otherwise false\n */\n return function isURLSameOrigin(requestURL) {\n var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;\n return (parsed.protocol === originURL.protocol &&\n parsed.host === originURL.host);\n };\n })() :\n\n // Non standard browser envs (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return function isURLSameOrigin() {\n return true;\n };\n })()\n);\n","'use strict';\n\nvar VERSION = require('../env/data').version;\n\nvar validators = {};\n\n// eslint-disable-next-line func-names\n['object', 'boolean', 'number', 'function', 'string', 'symbol'].forEach(function(type, i) {\n validators[type] = function validator(thing) {\n return typeof thing === type || 'a' + (i < 1 ? 'n ' : ' ') + type;\n };\n});\n\nvar deprecatedWarnings = {};\n\n/**\n * Transitional option validator\n * @param {function|boolean?} validator - set to false if the transitional option has been removed\n * @param {string?} version - deprecated version / removed since version\n * @param {string?} message - some message with additional info\n * @returns {function}\n */\nvalidators.transitional = function transitional(validator, version, message) {\n function formatMessage(opt, desc) {\n return '[Axios v' + VERSION + '] Transitional option \\'' + opt + '\\'' + desc + (message ? '. ' + message : '');\n }\n\n // eslint-disable-next-line func-names\n return function(value, opt, opts) {\n if (validator === false) {\n throw new Error(formatMessage(opt, ' has been removed' + (version ? ' in ' + version : '')));\n }\n\n if (version && !deprecatedWarnings[opt]) {\n deprecatedWarnings[opt] = true;\n // eslint-disable-next-line no-console\n console.warn(\n formatMessage(\n opt,\n ' has been deprecated since v' + version + ' and will be removed in the near future'\n )\n );\n }\n\n return validator ? validator(value, opt, opts) : true;\n };\n};\n\n/**\n * Assert object's properties type\n * @param {object} options\n * @param {object} schema\n * @param {boolean?} allowUnknown\n */\n\nfunction assertOptions(options, schema, allowUnknown) {\n if (typeof options !== 'object') {\n throw new TypeError('options must be an object');\n }\n var keys = Object.keys(options);\n var i = keys.length;\n while (i-- > 0) {\n var opt = keys[i];\n var validator = schema[opt];\n if (validator) {\n var value = options[opt];\n var result = value === undefined || validator(value, opt, options);\n if (result !== true) {\n throw new TypeError('option ' + opt + ' must be ' + result);\n }\n continue;\n }\n if (allowUnknown !== true) {\n throw Error('Unknown option ' + opt);\n }\n }\n}\n\nmodule.exports = {\n assertOptions: assertOptions,\n validators: validators\n};\n","'use strict';\n\nvar Cancel = require('./Cancel');\n\n/**\n * A `CancelToken` is an object that can be used to request cancellation of an operation.\n *\n * @class\n * @param {Function} executor The executor function.\n */\nfunction CancelToken(executor) {\n if (typeof executor !== 'function') {\n throw new TypeError('executor must be a function.');\n }\n\n var resolvePromise;\n\n this.promise = new Promise(function promiseExecutor(resolve) {\n resolvePromise = resolve;\n });\n\n var token = this;\n\n // eslint-disable-next-line func-names\n this.promise.then(function(cancel) {\n if (!token._listeners) return;\n\n var i;\n var l = token._listeners.length;\n\n for (i = 0; i < l; i++) {\n token._listeners[i](cancel);\n }\n token._listeners = null;\n });\n\n // eslint-disable-next-line func-names\n this.promise.then = function(onfulfilled) {\n var _resolve;\n // eslint-disable-next-line func-names\n var promise = new Promise(function(resolve) {\n token.subscribe(resolve);\n _resolve = resolve;\n }).then(onfulfilled);\n\n promise.cancel = function reject() {\n token.unsubscribe(_resolve);\n };\n\n return promise;\n };\n\n executor(function cancel(message) {\n if (token.reason) {\n // Cancellation has already been requested\n return;\n }\n\n token.reason = new Cancel(message);\n resolvePromise(token.reason);\n });\n}\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nCancelToken.prototype.throwIfRequested = function throwIfRequested() {\n if (this.reason) {\n throw this.reason;\n }\n};\n\n/**\n * Subscribe to the cancel signal\n */\n\nCancelToken.prototype.subscribe = function subscribe(listener) {\n if (this.reason) {\n listener(this.reason);\n return;\n }\n\n if (this._listeners) {\n this._listeners.push(listener);\n } else {\n this._listeners = [listener];\n }\n};\n\n/**\n * Unsubscribe from the cancel signal\n */\n\nCancelToken.prototype.unsubscribe = function unsubscribe(listener) {\n if (!this._listeners) {\n return;\n }\n var index = this._listeners.indexOf(listener);\n if (index !== -1) {\n this._listeners.splice(index, 1);\n }\n};\n\n/**\n * Returns an object that contains a new `CancelToken` and a function that, when called,\n * cancels the `CancelToken`.\n */\nCancelToken.source = function source() {\n var cancel;\n var token = new CancelToken(function executor(c) {\n cancel = c;\n });\n return {\n token: token,\n cancel: cancel\n };\n};\n\nmodule.exports = CancelToken;\n","'use strict';\n\n/**\n * Syntactic sugar for invoking a function and expanding an array for arguments.\n *\n * Common use case would be to use `Function.prototype.apply`.\n *\n * ```js\n * function f(x, y, z) {}\n * var args = [1, 2, 3];\n * f.apply(null, args);\n * ```\n *\n * With `spread` this example can be re-written.\n *\n * ```js\n * spread(function(x, y, z) {})([1, 2, 3]);\n * ```\n *\n * @param {Function} callback\n * @returns {Function}\n */\nmodule.exports = function spread(callback) {\n return function wrap(arr) {\n return callback.apply(null, arr);\n };\n};\n","'use strict';\n\n/**\n * Determines whether the payload is an error thrown by Axios\n *\n * @param {*} payload The value to test\n * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false\n */\nmodule.exports = function isAxiosError(payload) {\n return (typeof payload === 'object') && (payload.isAxiosError === true);\n};\n"],"sourceRoot":""} diff --git a/ResourceManager/src/main/resources/public/test.html b/ResourceManager/src/main/resources/public/test.html new file mode 100644 index 0000000..5eb6d7e --- /dev/null +++ b/ResourceManager/src/main/resources/public/test.html @@ -0,0 +1,37 @@ + + + + + Title + + + + +
+ 上传文件 + + + +
+
+ 图片预览 +
+
+ + +