需求背景
我們在美國服務器上部署了一個site,專門提供美國用戶使用,後端服務和文件服務都放在國內。用戶經常抱怨我們系統打開文件比較緩慢深圳打不開。我們經過測試發現,一般的service查詢在速度上都還是可以接受的,但是一些較大的文件,就會響應比較慢甚至出現打開失敗的情況。這是由於跨洲網絡出現丟包的現象導致的。
我們最初想到的方案是是開通VPN,通過專線來提高訪問速度。但是經過調研發現,VPN的收費太高了,我們小本經營根本承受不起。無奈之下,我們選擇通過程序將部分需要用到文件先同步到美國服務器,用戶訪問時通過nginx直接讀取當地的文件。
核心代碼
首先我們定義一個service用來接收推送過來的數據
因爲我們需要將文件的保存路徑與國內服務器一致,這樣前端纔好處理,所以我們使用form表單+附件流的方式。
@RequestMapping(value = "/attachment",method = RequestMethod.POST)
public ResponseTemplate saveFile(AttachmentFormDto dto){
ResponseTemplate template=new ResponseTemplate();
try{
attachmentService.saveFile(dto);
template.setCode(SUCCESS);
}catch (Exception e){
log.error(e.getMessage(),e);
template.setCode(FAILED);
template.setMsg(e.getMessage());
}
return template;
}
@Data
public class AttachmentFormDto {
/**
* 文件保存路徑
*/
private String filePath;
/**
* 文件顯示名稱
*/
private String showName;
/**
* 文件流
*/
private MultipartFile blFile;
}
保存文件這一塊代碼比較簡單,我們只需要將文件按照國內的路徑保存即可
@Override
public boolean saveFile(AttachmentFormDto dto) throws IOException {
String filePath=uploadPath+dto.getFilePath();
log.info("upload file path:{}",filePath);
File file=new File(filePath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
MultipartFile mpf=dto.getBlFile();
if(!mpf.isEmpty()){
mpf.transferTo(file);
}
log.info("文件保存路徑:{}",filePath);
return true;
}
接下就是客戶端的代碼,我們需要通過post請求將文件數據推送到遠程服務器。
//開始調用方法遠程傳送文件的到服務器
HttpResponse response=null;
CloseableHttpClient httpclient = HttpClients.custom().disableAutomaticRetries().build();
try{
String filePath=(uploadDir+"/"+x.getRealPath());
File file=new File(filePath);
String url=abroadHost+"/upload/attachment";
Logs.info("url>"+url);
InputStream in = new FileInputStream(file);
HttpPost httpPost = new HttpPost(url);
/**設置 contenttype 以form形式上傳文件, 強制使用UTF-8編碼**/
ContentType type=ContentType.MULTIPART_FORM_DATA.withCharset("UTF-8");
HttpEntity httpEntity = MultipartEntityBuilder.create()
.addPart("filePath", new StringBody(x.getRealPath(),type))
.addPart("showName",new StringBody(x.getShowName(),type))
.addPart("blFile",new FileBody(file)).build();
httpPost.setEntity(httpEntity);
RequestConfig requestConfig= RequestConfig.custom()
.setSocketTimeout(600000)
.setConnectTimeout(600000)
.build();
httpPost.setConfig(requestConfig);
response = httpclient.execute(httpPost);
int statusCode=response.getStatusLine().getStatusCode();
Logs.info("statusCode: {}",statusCode);
if(statusCode==200){
//更新到數據庫,標誌文件已經被推送
updateAtt(x);
}
String rsStr = EntityUtils.toString(response.getEntity());
Logs.info("response content is :=======================\n "+rsStr);
}catch (Exception e){
Logs.error(e.getMessage(),e);
}
在這裏有一個地方需要注意,爲了防止我們推送後的文件有重新修改,所以我們需要對文件信息進行md5,通過md5來判斷文件是有已經被推送和被修改過。
美國的site也會根據這個字段來判斷是否是讀取本地服務器的文件還是讀取國內的文件。