Commit f24cd534 authored by Menethil's avatar Menethil
Browse files

添加腾讯对象存储的实现

parent 61f6b986
...@@ -22,6 +22,13 @@ ...@@ -22,6 +22,13 @@
<artifactId>litemall-db</artifactId> <artifactId>litemall-db</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.4.4</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -7,14 +7,23 @@ import java.net.MalformedURLException; ...@@ -7,14 +7,23 @@ import java.net.MalformedURLException;
import java.nio.file.*; import java.nio.file.*;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.linlinjava.litemall.os.config.ObjectStorageConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service /**
* 服务器本地文件存储
*/
@Service("localStorage")
public class FileSystemStorageService implements StorageService { public class FileSystemStorageService implements StorageService {
@Autowired
private ObjectStorageConfig osConfig;
private static final Path rootLocation = Paths.get("storage"); private static final Path rootLocation = Paths.get("storage");
static { static {
try { try {
Files.createDirectories(rootLocation); Files.createDirectories(rootLocation);
...@@ -24,12 +33,11 @@ public class FileSystemStorageService implements StorageService { ...@@ -24,12 +33,11 @@ public class FileSystemStorageService implements StorageService {
} }
@Override @Override
public void store(InputStream inputStream, String filename) { public void store(MultipartFile file, String keyName) {
try { try {
Files.copy(inputStream, this.rootLocation.resolve(filename), StandardCopyOption.REPLACE_EXISTING); Files.copy(file.getInputStream(), this.rootLocation.resolve(keyName), StandardCopyOption.REPLACE_EXISTING);
} } catch (IOException e) {
catch (IOException e) { throw new RuntimeException("Failed to store file " + keyName, e);
throw new RuntimeException ("Failed to store file " + filename, e);
} }
} }
...@@ -39,9 +47,8 @@ public class FileSystemStorageService implements StorageService { ...@@ -39,9 +47,8 @@ public class FileSystemStorageService implements StorageService {
return Files.walk(this.rootLocation, 1) return Files.walk(this.rootLocation, 1)
.filter(path -> !path.equals(this.rootLocation)) .filter(path -> !path.equals(this.rootLocation))
.map(path -> this.rootLocation.relativize(path)); .map(path -> this.rootLocation.relativize(path));
} } catch (IOException e) {
catch (IOException e) { throw new RuntimeException("Failed to read stored files", e);
throw new RuntimeException ("Failed to read stored files", e);
} }
} }
...@@ -58,12 +65,10 @@ public class FileSystemStorageService implements StorageService { ...@@ -58,12 +65,10 @@ public class FileSystemStorageService implements StorageService {
Resource resource = new UrlResource(file.toUri()); Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) { if (resource.exists() || resource.isReadable()) {
return resource; return resource;
} } else {
else {
return null; return null;
} }
} } catch (MalformedURLException e) {
catch (MalformedURLException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
...@@ -79,4 +84,12 @@ public class FileSystemStorageService implements StorageService { ...@@ -79,4 +84,12 @@ public class FileSystemStorageService implements StorageService {
} }
} }
@Override
public String generateUrl(String keyName) {
String url = osConfig.getAddress() + ":" + osConfig.getPort() + "/os/storage/fetch/" + keyName;
if (!url.startsWith("http")) {
url = "http://" + url;
}
return url;
}
} }
\ No newline at end of file
package org.linlinjava.litemall.os.service; package org.linlinjava.litemall.os.service;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.stream.Stream; import java.util.stream.Stream;
/**
* 对象存储接口
*/
public interface StorageService { public interface StorageService {
void store(InputStream inputStream, String filename); /**
* 存储一个文件对象
* @param file SpringBoot MultipartFile文件对象
* @param keyName 文件索引名
*/
void store(MultipartFile file, String keyName);
Stream<Path> loadAll(); Stream<Path> loadAll();
Path load(String filename); Path load(String keyName);
Resource loadAsResource(String keyName);
Resource loadAsResource(String filename); void delete(String keyName);
void delete(String filename); String generateUrl(String keyName);
} }
\ No newline at end of file
package org.linlinjava.litemall.os.tencent;
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.linlinjava.litemall.os.service.StorageService;
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;
/**
* 腾讯对象存储服务类
*/
@PropertySource(value = "classpath:tencent.properties")
@Service("tencent")
public class TencentOSService implements StorageService {
@Value("${tencent.os.secretId}")
private String accessKey;
@Value("${tencent.os.secretKey}")
private String secretKey;
@Value("${tencent.os.region}")
private String region;
@Value("${tencent.os.bucketName}")
private String bucketName;
private COSClient cosClient;
public TencentOSService() {
}
private COSClient getCOSClient() {
if (cosClient == null) {
// 1 初始化用户身份信息(secretId, secretKey)
COSCredentials cred = new BasicCOSCredentials(accessKey, 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() {
//https://litemall-1256968571.cos-website.ap-guangzhou.myqcloud.com
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) {
System.console().printf(ex.getMessage());
}
}
@Override
public Stream<Path> 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;
}
}
...@@ -5,7 +5,6 @@ import org.linlinjava.litemall.core.util.ResponseUtil; ...@@ -5,7 +5,6 @@ import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.domain.LitemallStorage; import org.linlinjava.litemall.db.domain.LitemallStorage;
import org.linlinjava.litemall.db.service.LitemallStorageService; import org.linlinjava.litemall.db.service.LitemallStorageService;
import org.linlinjava.litemall.os.service.StorageService; import org.linlinjava.litemall.os.service.StorageService;
import org.linlinjava.litemall.os.config.ObjectStorageConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
...@@ -25,22 +24,11 @@ import java.util.Map; ...@@ -25,22 +24,11 @@ import java.util.Map;
@RequestMapping("/os/storage") @RequestMapping("/os/storage")
public class OsStorageController { public class OsStorageController {
@Autowired @javax.annotation.Resource(name="${activeStorage}")
private StorageService storageService; private StorageService storageService;
@Autowired @Autowired
private LitemallStorageService litemallStorageService; private LitemallStorageService litemallStorageService;
@Autowired
private ObjectStorageConfig osConfig;
private String generateUrl(String key){
String url = osConfig.getAddress() + ":" + osConfig.getPort() + "/os/storage/fetch/" + key;
if(!url.startsWith("http")){
url = "http://" + url;
}
return url;
}
private String generateKey(String originalFilename){ private String generateKey(String originalFilename){
int index = originalFilename.lastIndexOf('.'); int index = originalFilename.lastIndexOf('.');
String suffix = originalFilename.substring(index); String suffix = originalFilename.substring(index);
...@@ -82,9 +70,9 @@ public class OsStorageController { ...@@ -82,9 +70,9 @@ public class OsStorageController {
return ResponseUtil.badArgumentValue(); return ResponseUtil.badArgumentValue();
} }
String key = generateKey(originalFilename); String key = generateKey(originalFilename);
storageService.store(inputStream, key); storageService.store(file, key);
String url = generateUrl(key); String url = storageService.generateUrl(key);
LitemallStorage storageInfo = new LitemallStorage(); LitemallStorage storageInfo = new LitemallStorage();
storageInfo.setName(originalFilename); storageInfo.setName(originalFilename);
storageInfo.setSize((int)file.getSize()); storageInfo.setSize((int)file.getSize());
......
spring.profiles.active=dev spring.profiles.active=dev
server.port=8081 server.port=8081
logging.level.org.linlinjava.litemall.os.Application=DEBUG logging.level.org.linlinjava.litemall.os.Application=DEBUG
# \u5B58\u50A8\u5B9E\u73B0\uFF0C\u53EF\u9009\u62E9 localStorage \u6216\u8005 tencent \uFF0C\u5982\u679C\u9009\u62E9 tencent\uFF0C\u9700\u8981\u5F00\u901A\u817E\u8BAF\u5BF9\u8C61\u5B58\u50A8\u5E76\u914D\u7F6E tencent.properties
activeStorage=localStorage
#activeStorage=tencent
# \u817E\u8BAF\u4E91\u5B58\u50A8\u76F8\u5173\u914D\u7F6E,\u817E\u8BAF\u4E91\u5FC5\u987B\u6253\u5F00 #\u9759\u6001\u7F51\u7AD9(https://cloud.tencent.com/document/product/436/6249) \u652F\u6301\uFF0C\u5426\u5219\u56FE\u7247\u4F1A\u76F4\u63A5\u4E0B\u8F7D\u800C\u4E0D\u662F\u663E\u793A
tencent.os.secretId=""
tencent.os.secretKey=""
tencent.os.region=""
tencent.os.bucketName=""
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment