From 1072fc97bca3516618bebeb4b8c26449b51c201b Mon Sep 17 00:00:00 2001 From: Junling Bu Date: Sat, 21 Jul 2018 11:06:36 +0800 Subject: [PATCH] =?UTF-8?q?feat[litemall-core]:=20core=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AD=98=E5=82=A8=EF=BC=8C=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=AD=98=E5=82=A8=E3=80=81=E8=85=BE=E8=AE=AF?= =?UTF-8?q?=E4=BA=91=E5=AD=98=E5=82=A8=E3=80=81=E9=98=BF=E9=87=8C=E4=BA=91?= =?UTF-8?q?=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- litemall-core/.gitignore | 1 + litemall-core/pom.xml | 19 +++ .../litemall/core/storage/AliyunStorage.java | 132 +++++++++++++++ .../litemall/core/storage/LocalStorage.java | 119 ++++++++++++++ .../litemall/core/storage/Storage.java | 30 ++++ .../litemall/core/storage/StorageService.java | 60 +++++++ .../litemall/core/storage/TencentStorage.java | 138 ++++++++++++++++ .../config/StorageAutoConfiguration.java | 66 ++++++++ .../storage/config/StorageProperties.java | 151 ++++++++++++++++++ .../src/main/resources/application-core.yml | 25 ++- .../litemall/core/AliyunStorageTest.java | 39 +++++ .../litemall/core/LocalStorageTest.java | 40 +++++ .../litemall/core/TencentStorageTest.java | 39 +++++ litemall-core/src/test/resources/litemall.png | Bin 0 -> 8524 bytes 14 files changed, 858 insertions(+), 1 deletion(-) create mode 100644 litemall-core/src/main/java/org/linlinjava/litemall/core/storage/AliyunStorage.java create mode 100644 litemall-core/src/main/java/org/linlinjava/litemall/core/storage/LocalStorage.java create mode 100644 litemall-core/src/main/java/org/linlinjava/litemall/core/storage/Storage.java create mode 100644 litemall-core/src/main/java/org/linlinjava/litemall/core/storage/StorageService.java create mode 100644 litemall-core/src/main/java/org/linlinjava/litemall/core/storage/TencentStorage.java create mode 100644 litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageAutoConfiguration.java create mode 100644 litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageProperties.java create mode 100644 litemall-core/src/test/java/org/linlinjava/litemall/core/AliyunStorageTest.java create mode 100644 litemall-core/src/test/java/org/linlinjava/litemall/core/LocalStorageTest.java create mode 100644 litemall-core/src/test/java/org/linlinjava/litemall/core/TencentStorageTest.java create mode 100644 litemall-core/src/test/resources/litemall.png diff --git a/litemall-core/.gitignore b/litemall-core/.gitignore index 94ceef7d..a1d85ecf 100644 --- a/litemall-core/.gitignore +++ b/litemall-core/.gitignore @@ -1,3 +1,4 @@ /target/ /litemall-core.iml +/storage/ diff --git a/litemall-core/pom.xml b/litemall-core/pom.xml index 63812d53..bf3b8e4d 100644 --- a/litemall-core/pom.xml +++ b/litemall-core/pom.xml @@ -38,6 +38,25 @@ qcloudsms 1.0.5 + + + com.qcloud + cos_api + 5.4.4 + + + slf4j-log4j12 + org.slf4j + + + + + + com.aliyun.oss + aliyun-sdk-oss + 2.5.0 + + com.github.binarywang weixin-java-miniapp diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/AliyunStorage.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/AliyunStorage.java new file mode 100644 index 00000000..84c8c175 --- /dev/null +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/AliyunStorage.java @@ -0,0 +1,132 @@ +package org.linlinjava.litemall.core.storage; + +import com.aliyun.oss.OSSClient; +import com.aliyun.oss.model.ObjectMetadata; +import com.aliyun.oss.model.PutObjectRequest; +import com.aliyun.oss.model.PutObjectResult; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.web.multipart.MultipartFile; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.util.stream.Stream; + +/** + * @author Yogeek + * @date 2018/7/16 16:10 + * @decrpt 阿里云对象存储服务 + */ +public class AliyunStorage implements Storage { + + private String endpoint; + private String accessKeyId; + private String accessKeySecret; + private String bucketName; + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getAccessKeyId() { + return accessKeyId; + } + + public void setAccessKeyId(String accessKeyId) { + this.accessKeyId = accessKeyId; + } + + public String getAccessKeySecret() { + return accessKeySecret; + } + + public void setAccessKeySecret(String accessKeySecret) { + this.accessKeySecret = accessKeySecret; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + /** + * 获取阿里云OSS客户端对象 + * + * @return ossClient + */ + private OSSClient getOSSClient(){ + return new OSSClient(endpoint,accessKeyId, accessKeySecret); + } + + private String getBaseUrl() { + return "https://" + bucketName + "." + endpoint + "/" ; + } + + /** + * 阿里云OSS对象存储简单上传实现 + */ + @Override + public void store(MultipartFile file, String keyName) { + try { + // 简单文件上传, 最大支持 5 GB, 适用于小文件上传, 建议 20M以下的文件使用该接口 + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(file.getSize()); + objectMetadata.setContentType(file.getContentType()); + // 对象键(Key)是对象在存储桶中的唯一标识。 + PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, keyName, file.getInputStream(), objectMetadata); + PutObjectResult putObjectResult = getOSSClient().putObject(putObjectRequest); + } catch (Exception ex) { + ex.printStackTrace(); + } + + } + + @Override + public Stream loadAll() { + return null; + } + + @Override + public Path load(String keyName) { + return null; + } + + @Override + public Resource loadAsResource(String keyName) { + try { + URL url = new URL(getBaseUrl() + keyName); + Resource resource = new UrlResource(url); + if (resource.exists() || resource.isReadable()) { + return resource; + } else { + return null; + } + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void delete(String keyName) { + try { + getOSSClient().deleteObject(bucketName, keyName); + }catch (Exception e){ + e.printStackTrace(); + } + + } + + @Override + public String generateUrl(String keyName) { + return getBaseUrl() + keyName; + } +} diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/LocalStorage.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/LocalStorage.java new file mode 100644 index 00000000..e2922546 --- /dev/null +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/LocalStorage.java @@ -0,0 +1,119 @@ +package org.linlinjava.litemall.core.storage; + + +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.stream.Stream; + +/** + * 服务器本地对象存储服务 + */ +public class LocalStorage implements Storage { + + private String storagePath; + private String address; + private String port; + + private Path rootLocation; + + public String getStoragePath() { + return storagePath; + } + + public void setStoragePath(String storagePath) { + this.storagePath = storagePath; + + this.rootLocation = Paths.get(storagePath); + try { + Files.createDirectories(rootLocation); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + + @Override + public void store(MultipartFile file, String keyName) { + try { + Files.copy(file.getInputStream(), rootLocation.resolve(keyName), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException("Failed to store file " + keyName, e); + } + } + + @Override + public Stream loadAll() { + try { + return Files.walk(rootLocation, 1) + .filter(path -> !path.equals(rootLocation)) + .map(path -> rootLocation.relativize(path)); + } catch (IOException e) { + throw new RuntimeException("Failed to read stored files", e); + } + + } + + @Override + public Path load(String filename) { + return rootLocation.resolve(filename); + } + + @Override + public Resource loadAsResource(String filename) { + try { + Path file = load(filename); + Resource resource = new UrlResource(file.toUri()); + if (resource.exists() || resource.isReadable()) { + return resource; + } else { + return null; + } + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void delete(String filename) { + Path file = load(filename); + try { + Files.delete(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public String generateUrl(String keyName) { + String url = address + ":" + port + "/os/storage/fetch/" + keyName; + if (!url.startsWith("http")) { + url = "http://" + url; + } + return url; + } +} \ No newline at end of file diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/Storage.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/Storage.java new file mode 100644 index 00000000..58a769ef --- /dev/null +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/Storage.java @@ -0,0 +1,30 @@ +package org.linlinjava.litemall.core.storage; + +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Path; +import java.util.stream.Stream; + +/** + * 对象存储接口 + */ +public interface Storage { + + /** + * 存储一个文件对象 + * @param file SpringBoot MultipartFile文件对象 + * @param keyName 文件索引名 + */ + void store(MultipartFile file, String keyName); + + Stream loadAll(); + + Path load(String keyName); + + Resource loadAsResource(String keyName); + + void delete(String keyName); + + String generateUrl(String keyName); +} \ No newline at end of file diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/StorageService.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/StorageService.java new file mode 100644 index 00000000..f6cee6f9 --- /dev/null +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/StorageService.java @@ -0,0 +1,60 @@ +package org.linlinjava.litemall.core.storage; + +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Path; +import java.util.Map; +import java.util.stream.Stream; + +public class StorageService { + private String active; + private Storage storage; + private Map supportedStorage; + + public String getActive() { + return active; + } + + public void setActive(String active) { + this.active = active; + this.storage = this.supportedStorage.get(active); + } + + public Map getSupportedStorage() { + return supportedStorage; + } + + public void setSupportedStorage(Map supportedStorage) { + this.supportedStorage = supportedStorage; + } + + /** + * 存储一个文件对象 + * @param file SpringBoot MultipartFile文件对象 + * @param keyName 文件索引名 + */ + public void store(MultipartFile file, String keyName) { + storage.store(file, keyName); + } + + public Stream loadAll() { + return storage.loadAll(); + } + + public Path load(String keyName) { + return storage.load(keyName); + } + + public Resource loadAsResource(String keyName){ + return storage.loadAsResource(keyName); + } + + public void delete(String keyName){ + storage.delete(keyName); + } + + public String generateUrl(String keyName) { + return storage.generateUrl(keyName); + } +} diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/TencentStorage.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/TencentStorage.java new file mode 100644 index 00000000..505e270a --- /dev/null +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/TencentStorage.java @@ -0,0 +1,138 @@ +package org.linlinjava.litemall.core.storage; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectRequest; +import com.qcloud.cos.model.PutObjectResult; +import com.qcloud.cos.region.Region; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.util.stream.Stream; + +/** + * 腾讯对象存储服务 + */ +public class TencentStorage implements Storage { + + private String secretId; + private String secretKey; + private String region; + private String bucketName; + + private COSClient cosClient; + + public String getSecretId() { + return secretId; + } + + public void setSecretId(String secretId) { + this.secretId = secretId; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + private COSClient getCOSClient() { + if (cosClient == null) { + // 1 初始化用户身份信息(secretId, secretKey) + COSCredentials cred = new BasicCOSCredentials(secretId, secretKey); + // 2 设置bucket的区域, COS地域的简称请参照 https://cloud.tencent.com/document/product/436/6224 + ClientConfig clientConfig = new ClientConfig(new Region(region)); + cosClient = new COSClient(cred, clientConfig); + } + + return cosClient; + } + + private String getBaseUrl() { + return "https://" + bucketName + ".cos-website." + region + ".myqcloud.com/"; + } + + @Override + public void store(MultipartFile file, String keyName) { + try { + // 简单文件上传, 最大支持 5 GB, 适用于小文件上传, 建议 20M以下的文件使用该接口 + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(file.getSize()); + objectMetadata.setContentType(file.getContentType()); + // 对象键(Key)是对象在存储桶中的唯一标识。例如,在对象的访问域名 `bucket1-1250000000.cos.ap-guangzhou.myqcloud.com/doc1/pic1.jpg` 中,对象键为 doc1/pic1.jpg, 详情参考 [对象键](https://cloud.tencent.com/document/product/436/13324) + PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, keyName, file.getInputStream(), objectMetadata); + PutObjectResult putObjectResult = getCOSClient().putObject(putObjectRequest); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public Stream loadAll() { + return null; + } + + @Override + public Path load(String keyName) { + return null; + } + + @Override + public Resource loadAsResource(String keyName) { + try { + URL url = new URL(getBaseUrl() + keyName); + Resource resource = new UrlResource(url); + if (resource.exists() || resource.isReadable()) { + return resource; + } else { + return null; + } + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void delete(String keyName) { + try { + getCOSClient().deleteObject(bucketName, keyName); + }catch (Exception e){ + e.printStackTrace(); + } + + } + + @Override + public String generateUrl(String keyName) { + return getBaseUrl() + keyName; + } +} diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageAutoConfiguration.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageAutoConfiguration.java new file mode 100644 index 00000000..a9d08f41 --- /dev/null +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageAutoConfiguration.java @@ -0,0 +1,66 @@ +package org.linlinjava.litemall.core.storage.config; + +import org.apache.commons.collections.map.HashedMap; +import org.linlinjava.litemall.core.storage.*; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +@EnableConfigurationProperties(StorageProperties.class) +public class StorageAutoConfiguration { + + private final StorageProperties properties; + + public StorageAutoConfiguration(StorageProperties properties) { + this.properties = properties; + } + + @Bean + public StorageService storageService() { + StorageService storageService = new StorageService(); + Map supportedStorage = new HashMap(3); + supportedStorage.put("local", localStorage()); + supportedStorage.put("aliyun", aliyunStorage()); + supportedStorage.put("tencent", tencentStorage()); + storageService.setSupportedStorage(supportedStorage); + String active = this.properties.getActive(); + storageService.setActive(active); + return storageService; + } + + @Bean + public LocalStorage localStorage() { + LocalStorage localStorage = new LocalStorage(); + StorageProperties.Local local = this.properties.getLocal(); + localStorage.setAddress(local.getAddress()); + localStorage.setPort(local.getPort()); + localStorage.setStoragePath(local.getStoragePath()); + return localStorage; + } + + @Bean + public AliyunStorage aliyunStorage() { + AliyunStorage aliyunStorage = new AliyunStorage(); + StorageProperties.Aliyun aliyun = this.properties.getAliyun(); + aliyunStorage.setAccessKeyId(aliyun.getAccessKeyId()); + aliyunStorage.setAccessKeySecret(aliyun.getAccessKeySecret()); + aliyunStorage.setBucketName(aliyun.getBucketName()); + aliyunStorage.setEndpoint(aliyun.getEndpoint()); + return aliyunStorage; + } + + @Bean + public TencentStorage tencentStorage() { + TencentStorage tencentStorage = new TencentStorage(); + StorageProperties.Tencent tencent = this.properties.getTencent(); + tencentStorage.setSecretId(tencent.getSecretId()); + tencentStorage.setSecretKey(tencent.getSecretKey()); + tencentStorage.setBucketName(tencent.getBucketName()); + tencentStorage.setRegion(tencent.getRegion()); + return tencentStorage; + } +} diff --git a/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageProperties.java b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageProperties.java new file mode 100644 index 00000000..b8b21f0d --- /dev/null +++ b/litemall-core/src/main/java/org/linlinjava/litemall/core/storage/config/StorageProperties.java @@ -0,0 +1,151 @@ +package org.linlinjava.litemall.core.storage.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "litemall.storage") +public class StorageProperties { + private String active; + private Local local; + private Aliyun aliyun; + private Tencent tencent; + + public String getActive() { + return active; + } + + public void setActive(String active) { + this.active = active; + } + + public Local getLocal() { + return local; + } + + public void setLocal(Local local) { + this.local = local; + } + + public Aliyun getAliyun() { + return aliyun; + } + + public void setAliyun(Aliyun aliyun) { + this.aliyun = aliyun; + } + + public Tencent getTencent() { + return tencent; + } + + public void setTencent(Tencent tencent) { + this.tencent = tencent; + } + + public static class Local { + private String address; + private String port; + private String storagePath; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getStoragePath() { + return storagePath; + } + + public void setStoragePath(String storagePath) { + this.storagePath = storagePath; + } + } + + public static class Tencent { + private String secretId; + private String secretKey; + private String region; + private String bucketName; + + public String getSecretId() { + return secretId; + } + + public void setSecretId(String secretId) { + this.secretId = secretId; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + } + + public static class Aliyun { + private String endpoint; + private String accessKeyId; + private String accessKeySecret; + private String bucketName; + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getAccessKeyId() { + return accessKeyId; + } + + public void setAccessKeyId(String accessKeyId) { + this.accessKeyId = accessKeyId; + } + + public String getAccessKeySecret() { + return accessKeySecret; + } + + public void setAccessKeySecret(String accessKeySecret) { + this.accessKeySecret = accessKeySecret; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + } +} diff --git a/litemall-core/src/main/resources/application-core.yml b/litemall-core/src/main/resources/application-core.yml index f2045547..7ee80e44 100644 --- a/litemall-core/src/main/resources/application-core.yml +++ b/litemall-core/src/main/resources/application-core.yml @@ -70,4 +70,27 @@ litemall: - code: "FEDEX" name: "FEDEX联邦(国内件)" - code: "FEDEX_GJ" - name: "FEDEX联邦(国际件)" \ No newline at end of file + name: "FEDEX联邦(国际件)" + + # 对象存储配置 + storage: + # 当前工作的对象存储模式,分别是local、aliyun、tencent + active: local + # 本地对象存储配置信息 + local: + storagePath: storage + address: http://127.0.0.1 + port: 8081 + # 阿里云对象存储配置信息 + aliyun: + endpoint: oss-cn-shenzhen.aliyuncs.com + accessKeyId: 111111 + accessKeySecret: xxxxxx + bucketName: xxxxxx + # 腾讯对象存储配置信息 + # 请参考 https://cloud.tencent.com/document/product/436/6249 + tencent: + secretId: 111111 + secretKey: xxxxxx + region: xxxxxx + bucketName: xxxxxx \ No newline at end of file diff --git a/litemall-core/src/test/java/org/linlinjava/litemall/core/AliyunStorageTest.java b/litemall-core/src/test/java/org/linlinjava/litemall/core/AliyunStorageTest.java new file mode 100644 index 00000000..e5a949eb --- /dev/null +++ b/litemall-core/src/test/java/org/linlinjava/litemall/core/AliyunStorageTest.java @@ -0,0 +1,39 @@ +package org.linlinjava.litemall.core; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.linlinjava.litemall.core.storage.AliyunStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.Resource; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.util.FileCopyUtils; + +import java.io.FileInputStream; +import java.io.IOException; + +@WebAppConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest +public class AliyunStorageTest { + @Autowired + private AliyunStorage aliyunStorage; + + @Test + public void test() throws IOException { + String test = getClass().getClassLoader().getResource("litemall.png").getFile(); + byte[] content = (byte[])FileCopyUtils.copyToByteArray(new FileInputStream(test)); + MockMultipartFile mockMultipartFile = new MockMultipartFile("litemall.png", "litemall.png", "image/png", content); + aliyunStorage.store(mockMultipartFile, "litemall.png"); + Resource resource = aliyunStorage.loadAsResource("litemall.png"); + String url = aliyunStorage.generateUrl("litemall.png"); + System.out.println("test file " + test); + System.out.println("store file " + resource.getURI()); + System.out.println("generate url " + url); + +// tencentOsService.delete("litemall.png"); + } + +} \ No newline at end of file diff --git a/litemall-core/src/test/java/org/linlinjava/litemall/core/LocalStorageTest.java b/litemall-core/src/test/java/org/linlinjava/litemall/core/LocalStorageTest.java new file mode 100644 index 00000000..aca78d04 --- /dev/null +++ b/litemall-core/src/test/java/org/linlinjava/litemall/core/LocalStorageTest.java @@ -0,0 +1,40 @@ +package org.linlinjava.litemall.core; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.linlinjava.litemall.core.storage.LocalStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.Resource; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.util.FileCopyUtils; + +import java.io.FileInputStream; +import java.io.IOException; + +@WebAppConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest +public class LocalStorageTest { + @Autowired + private LocalStorage localStorage; + + @Test + public void test() throws IOException { + String test = getClass().getClassLoader().getResource("litemall.png").getFile(); + byte[] content = (byte[])FileCopyUtils.copyToByteArray(new FileInputStream(test)); + MockMultipartFile mockMultipartFile = new MockMultipartFile("litemall.png", "litemall.png", "image/jpeg", content); + localStorage.store(mockMultipartFile, "litemall.png"); + Resource resource = localStorage.loadAsResource("litemall.png"); + String url = localStorage.generateUrl("litemall.png"); + System.out.println("test file " + test); + System.out.println("store file " + resource.getURI()); + System.out.println("generate url " + url); + +// localStorage.delete("litemall.png"); + + } + +} \ No newline at end of file diff --git a/litemall-core/src/test/java/org/linlinjava/litemall/core/TencentStorageTest.java b/litemall-core/src/test/java/org/linlinjava/litemall/core/TencentStorageTest.java new file mode 100644 index 00000000..fae0a9a9 --- /dev/null +++ b/litemall-core/src/test/java/org/linlinjava/litemall/core/TencentStorageTest.java @@ -0,0 +1,39 @@ +package org.linlinjava.litemall.core; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.linlinjava.litemall.core.storage.TencentStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.Resource; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.util.FileCopyUtils; + +import java.io.FileInputStream; +import java.io.IOException; + +@WebAppConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest +public class TencentStorageTest { + @Autowired + private TencentStorage tencentStorage; + + @Test + public void test() throws IOException { + String test = getClass().getClassLoader().getResource("litemall.png").getFile(); + byte[] content = (byte[])FileCopyUtils.copyToByteArray(new FileInputStream(test)); + MockMultipartFile mockMultipartFile = new MockMultipartFile("litemall.png", "litemall.png", "image/png", content); + tencentStorage.store(mockMultipartFile, "litemall.png"); + Resource resource = tencentStorage.loadAsResource("litemall.png"); + String url = tencentStorage.generateUrl("litemall.png"); + System.out.println("test file " + test); + System.out.println("store file " + resource.getURI()); + System.out.println("generate url " + url); + +// tencentStorage.delete("litemall.png"); + } + +} \ No newline at end of file diff --git a/litemall-core/src/test/resources/litemall.png b/litemall-core/src/test/resources/litemall.png new file mode 100644 index 0000000000000000000000000000000000000000..d16e9afa0012582aa308a4807f857195301382d5 GIT binary patch literal 8524 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 literal 0 HcmV?d00001