動靜分離-靜態資源緩存控制

1、靜態資源服務與動態資源服務的區別

首先動靜分離非先後端分離,關於二者的介紹以下:css

  • 動靜分離:動態資源(jsp、ftl)與靜態資源(js、img、css)分開
  • 先後端分離:接口與視圖分開獨立開發部署

2、爲何靜態資源須要實現CDN內容加速

在一個網站中,請求是比較佔寬帶資源的。java

其主要加載內容爲靜態資源,如: css、js、imgnginx

咱們知道,一個1兆帶寬服務器 = 128kb/s,若是存在一個 512/kb 的靜態資源須要請求 4s 左右,而動態資源(json)佔帶寬很小(幾十B),幾乎能夠忽略不計。數據庫

既然帶寬影響網站訪問速度,那就加帶寬好了?json

可是!帶寬價格不是貴的一點點…後端

因此市場上出現了一些靜態資源服務器平臺(對象文件存儲) 。跨域

好比七牛雲、阿里雲(oss)、騰訊雲(內置CDN內容分發)。服務器

什麼是CDN內容分發?

CDN內容分發,就是將靜態資源服務器會部署全國各個服務器節點,用戶訪問的時候,遵循就近原則。好比你的所在地在濟南,那麼你在訪問資源文件時,會分配給你距離濟南最近的CDN網點。微信

3、七牛雲建立靜態資源存儲空間

官方地址:https://developer.qiniu.com/架構

註冊以後就能夠去建立對象存儲空間了,詳細步驟建議看官方文檔。

建立好空間後須要綁定一個本身的域名,若是不綁定則使用默認提供的域名,提供的域名有默認的使用時長:

以下是個人存儲空間域名綁定截圖:

以下是我以前塗塗影院存放的一些靜態資源:

4、動靜分離架構系統缺點

使用CDN內容分發確實提升了網站的訪問速度,可是動靜分離架構模式有什麼缺點呢?

跨域問題

好比域名訪問的是:www.sscai.club

而靜態資源訪問的則是:cdn.sscai.club

如何解決跨域問題?

nginx轉發

將 www.sscai.club 轉發到 cdn.sscai.club

5、代碼中實現七牛雲文件上傳

引入pom依賴

<!-- 七牛雲SDK -->
<dependency>
    <groupId>com.qiniu</groupId>
    <artifactId>qiniu-java-sdk</artifactId>
    <version>[7.2.0, 7.2.99]</version>
</dependency>

主要代碼:

@RequestMapping(value = "/file", method = RequestMethod.POST)
@ApiOperation(value = "文件上傳")
public Result<Object> upload(
    @RequestParam(required = false) MultipartFile file,
    @RequestParam(required = false) String base64,
    HttpServletRequest request) {

    // 判斷上傳類型 */
    if(StrUtil.isNotBlank(base64)){
        // base64上傳 */
        file = Base64DecodeMultipartFile.base64Convert(base64);
    }
    String result = "";
    String fKey = renamePic(file.getOriginalFilename());
    File f = new File();
    try {
        // 獲取文件流
        InputStream inputStream = file.getInputStream();

        /* 上傳至第三方雲服務——七牛雲 */
        result = qiniuInputStreamUpload(inputStream, fKey);
        f.setLocation(CommonConstant.OSS_QINIU);

        /* 保存數據信息至數據庫 */
        f.setName(file.getOriginalFilename());
        f.setSize(file.getSize());
        f.setType(file.getContentType());
        f.setFKey(fKey);
        f.setUrl(result);
        fileService.save(f);
    } catch (Exception e) {
        log.error(e.toString());
        return new ResultUtil<Object>().setErrorMsg(e.toString());
    }
    if(used.equals(SettingConstant.LOCAL_OSS)){
        OssSetting os = fileUtil.getOssSetting();
        result = os.getHttp() + os.getEndpoint() + "/" + f.getId();
    }
    return new ResultUtil<Object>().setData(result);
}

public static String renamePic(String fileName) {
    String extName = fileName.substring(fileName.lastIndexOf("."));
    return UUID.randomUUID().toString().replace("-""") + extName;
}


/**
 * 文件流上傳
 * @param inputStream
 * @param key  文件名
 * @return
 */

public String qiniuInputStreamUpload(InputStream inputStream, String key) {

    OssSetting os = getOssSetting();
    Auth auth = Auth.create(os.getAccessKey(), os.getSecretKey());
    String upToken = auth.uploadToken(os.getBucket());
    try {
        Response response = getUploadManager(getConfiguration(os.getZone())).put(inputStream, key, upToken, nullnull);
        /* 解析上傳成功的結果 */
        DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
        return os.getHttp() + os.getEndpoint() + "/" + putRet.key;
    } catch (QiniuException ex) {
        Response r = ex.response;
        throw new XbootException("上傳文件出錯,請檢查七牛雲配置," + r.toString());
    }
}


@Data
public class OssSetting implements Serializable{

    @ApiModelProperty(value = "服務商")
    private String serviceName;

    @ApiModelProperty(value = "ak")
    private String accessKey;

    @ApiModelProperty(value = "sk")
    private String secretKey;

    @ApiModelProperty(value = "endpoint域名")
    private String endpoint;

    @ApiModelProperty(value = "bucket空間")
    private String bucket;

    @ApiModelProperty(value = "http")
    private String http;

    @ApiModelProperty(value = "zone存儲區域")
    private Integer zone;

    @ApiModelProperty(value = "bucket存儲區域")
    private String bucketRegion;

    @ApiModelProperty(value = "本地存儲路徑")
    private String filePath;

    @ApiModelProperty(value = "是否改變secrectKey")
    private Boolean changed;
}

Base64轉爲MultipartFile工具類:

/**
 * base64轉爲multipartFile工具類
 * @author nikou
 */

public class Base64DecodeMultipartFile implements MultipartFile {

    private final byte[] imgContent;
    private final String header;

    public Base64DecodeMultipartFile(byte[] imgContent, String header) {
        this.imgContent = imgContent;
        this.header = header.split(";")[0]; 
    }

    @Override
    public String getName() {
        return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
    }

    @Override
    public String getOriginalFilename() {
        return System.currentTimeMillis() + (int)Math.random() * 10000 + "." + header.split("/")[1];
    }

    @Override
    public String getContentType() {
        return header.split(":")[1];
    }

    @Override
    public boolean isEmpty() {
        return imgContent == null || imgContent.length == 0;
    }

    @Override
    public long getSize() {
        return imgContent.length;
    }

    @Override
    public byte[] getBytes() throws IOException {
        return imgContent;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(imgContent);
    }

    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        new FileOutputStream(dest).write(imgContent);
    }


    public static MultipartFile base64Convert(String base64) {

        String[] baseStrs = base64.split(",");
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] b = new byte[0];
        try {
            b = decoder.decodeBuffer(baseStrs[1]);
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < b.length; ++i) {
            if (b[i] < 0) {
                b[i] += 256;
            }
        }
        return new Base64DecodeMultipartFile(b, baseStrs[0]);
    }
}

我建立了一個java相關的公衆號,用來記錄本身的學習之路,感興趣的小夥伴能夠關注一下微信公衆號哈:niceyoo

相關文章
相關標籤/搜索