文章目錄
靜態化優化方案設計
爲什麼要使用頁面靜態化
課程主頁的訪問人數非常多, 以不發請求靜態頁面代替要發請求靜態頁面或者動態頁面.沒有對後臺數據獲取.
課程詳情頁:只要課程信息不改,詳情頁就不會改變.
官網主頁:一定的時間段是不可變
招聘主頁:一定的時間段是不可變
職位詳情:只要職位信息不改,詳情頁就不會改變.
有的頁面訪問人數很多,但是在一定時間段內不會改變(數據沒變化).頁面靜態化.
靜態化好處
降低數據庫或緩存壓力
提高響應速度,增強用戶體驗.
分析
頁面靜態化是這一種方案,而模板技術是實現這種方案的技術
靜態頁面=模板(結構)+數據(內容)
靜態頁面生成時機
①當部署並啓動,需要在後臺管理裏面觸發一個按鈕,初始化靜態頁面. 初始化
②當數據(類型,廣告等)發生改變後(後臺做了增刪改),要覆蓋原來靜態頁面. 替換
方案:頁面靜態化,通過模板技術來實現.
模板技術:freemaker,velocity,thymeleaf等
單體項目方案分析
集羣項目架構分析
實現準備
新建模塊 hrm-page-parent
需要的配置
首先在網關中添加
然後是Swagger
數據庫
爲了數據庫查詢的一個效率 我們採用了反三範式的設計
數據庫說明:
t_site:前端站點 主站 課程 職位 要做頁面靜態化的前端網站
t_page 頁面, 在站點下面有多個頁面要做靜態化。 定義了模板
t_page_config 每做一次頁面靜態化,都要添加一個對象。一個頁面一次持久化所需要數據保存。
生成代碼
此處省略
技術準備
模板技術 velocity
RabbitMq
Nosql — redis
Dfs技術-fastdfs
Fastdfs
使用feign實現文件的上傳和下載 放在公共的服務裏面
hrm-common-common需要導入的依賴
<!--客戶端feign支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>2.1.0</version>
</dependency>
hrm-common-client 模塊
注意 這裏使用的配置是我們自己的配置
configuration = FeignMultipartSupportConfig.class
FastDfsClient.java
@FeignClient(value = "HRM-COMMON", configuration = FeignMultipartSupportConfig.class,
fallbackFactory = FastDfsClientFallbackFactory.class )
@RequestMapping("/fastDfs")
public interface FastDfsClient {
//上傳
@PostMapping(produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
AjaxResult upload(@RequestPart(required = true,value = "file")MultipartFile file);
//下載 以Response來接收數據
@GetMapping
Response download(@RequestParam(required = true,value = "path") String path);
}
FastDfsClientFallbackFactory.java
@Component
public class FastDfsClientFallbackFactory implements FallbackFactory<FastDfsClient> {
@Override
public FastDfsClient create(Throwable throwable) {
return new FastDfsClient() {
@Override
public AjaxResult upload(MultipartFile file) {
return new AjaxResult().setSuccess(false).setMessage("上傳失敗");
}
@Override
public Response download(String path) {
return null;
}
};
}
}
FeignMultipartSupportConfig.java
@Configuration
public class FeignMultipartSupportConfig {
//form編碼格式
@Bean
@Primary
@Scope("prototype")
public Encoder multipartFormEncoder() {
return new SpringFormEncoder();
}
@Bean
public feign.Logger.Level multipartLoggerLevel() {
return feign.Logger.Level.FULL;
}
}
hrm-common-service-2090模塊 上傳下載的實現
需要導入的依賴
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
實現
FastDfsController.java
@RestController
@RequestMapping("/fastDfs")
public class FastDfsController {
//crud 上傳 下載(查看) 刪除 修改(先刪除後添加)
/**
* 參數:上傳文件
* 返回值:成功與否,還要返回地址
*/
@PostMapping(produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AjaxResult upload(@RequestPart(required = true,value = "file")MultipartFile file){
try {
System.out.println(file.getOriginalFilename() + ":" + file.getSize());
String originalFilename = file.getOriginalFilename();
// xxx.jpg
String extName = originalFilename.substring(originalFilename.lastIndexOf(".")+1);
System.out.println(extName);
String filePath = FastDfsApiOpr.upload(file.getBytes(), extName);
return AjaxResult.me().setResultObj(filePath); //把上傳後的路徑返回回去
} catch (IOException e) {
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage("上傳失敗!"+e.getMessage());
}
}
//下載
@GetMapping
void download(@RequestParam(required = true,value = "path") String path, HttpServletResponse response){
ByteArrayInputStream bis = null;
ServletOutputStream outputStream = null;
try{
String pathTmp = path.substring(1); // goup1/xxxxx/yyyy
String groupName = pathTmp.substring(0, pathTmp.indexOf("/")); //goup1
String remotePath = pathTmp.substring(pathTmp.indexOf("/")+1);// xxxxx/yyyy
System.out.println(groupName);
System.out.println(remotePath);
byte[] data = FastDfsApiOpr.download(groupName, remotePath);
//以流方式直接做響應 不需要返回Response
bis = new ByteArrayInputStream(data);
outputStream = response.getOutputStream();
IOUtils.copy(bis,outputStream);
}catch (Exception e
){
e.printStackTrace();
}
finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 參數:完整路徑 /goup1/xxxxx/yyyy
* 返回值:成功與否,還要返回地址
*
*/
@DeleteMapping
public AjaxResult del(@RequestParam(required = true,value = "path") String path){
String pathTmp = path.substring(1); // goup1/xxxxx/yyyy
String groupName = pathTmp.substring(0, pathTmp.indexOf("/")); //goup1
String remotePath = pathTmp.substring(pathTmp.indexOf("/")+1);// /xxxxx/yyyy
System.out.println(groupName);
System.out.println(remotePath);
FastDfsApiOpr.delete(groupName, remotePath);
return AjaxResult.me();
}
}
FastDfsApiOpr.java在上一篇文章中有寫到
Velocity
Velocity是一個基於java的模板引擎(template engine),它允許任何人僅僅簡單的使用模板語言(template language)來引用由java代碼定義的對象。作爲一個比較完善的模板引擎,Velocity的功能是比較強大的,但強大的同時也增加了應用複雜性。
準備模板
準備前端頁面
Pager.vue
基本上的準備都已完成 接下來我們正式開始
頁面靜態化的整體流程
後端代碼
搭建一個hrm-page-parent模塊
也是三個子模塊
hrm-page-common模塊放domain 和Query
導入依賴 其餘兩個模塊也是依賴這個模塊
<dependencies>
<!--不能直接依賴starter,有自動配置,而消費者是不需要額。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.leryoo</groupId>
<artifactId>hrm-basic-util</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--不能全部引入mybatis-plus,這是要做數據庫操作,這裏是不需要的,只需引入核心包解決錯誤而已-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
hrm-page-service-2030
首先是配置文件
bootstrap.yml
spring:
profiles:
active: dev
cloud:
config:
name: application-page #github上面名稱
profile: ${spring.profiles.active} #環境 java -jar -D xxx jar
label: master #分支
discovery:
enabled: true #從eureka上面找配置服務
service-id: hrm-config-server #指定服務名
#uri: http://127.0.0.1:1299 #配置服務器 單機配置
eureka: #eureka不能放到遠程配置中
client:
service-url:
defaultZone: http://localhost:1010/eureka #告訴服務提供者要把服務註冊到哪兒 #單機環境
instance:
prefer-ip-address: true #顯示客戶端真實ip
feign:
hystrix:
enabled: true #開啓熔斷支持
client:
config:
remote-service: #服務名,填寫default爲所有服務
connectTimeout: 3000
readTimeout: 3000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
在碼雲的配置倉庫中添加
application-page-dev.yml
server:
port: 2030
spring:
application:
name: hrm-page
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/hrm-page
username: root
password: root
mybatis-plus:
mapper-locations: classpath:org/leryoo/mapper/*Mapper.xml
type-aliases-package: org.leryoo.domain,org.leryoo.query
注意 記得要先提交到碼雲
然後是導入兩個工具類
一個是文件解壓的工具類
ZipUtil.java
import java.io.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public final class ZipUtil
{
/**
* 緩衝大小
*/
private static int BUFFERSIZE = 2 << 10;
/**
* 壓縮
* @param paths
* @param fileName
*/
public static void zip(String[] paths, String fileName)
{
ZipOutputStream zos = null;
try
{
zos = new ZipOutputStream(new FileOutputStream(fileName));
for(String filePath : paths)
{
//遞歸壓縮文件
File file = new File(filePath);
String relativePath = file.getName();
if(file.isDirectory())
{
relativePath += File.separator;
}
zipFile(file, relativePath, zos);
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if(zos != null)
{
zos.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
public static void zipFile(File file, String relativePath, ZipOutputStream zos)
{
InputStream is = null;
try
{
if(!file.isDirectory())
{
ZipEntry zp = new ZipEntry(relativePath);
zos.putNextEntry(zp);
is = new FileInputStream(file);
byte[] buffer = new byte[BUFFERSIZE];
int length = 0;
while ((length = is.read(buffer)) >= 0)
{
zos.write(buffer, 0, length);
}
zos.flush();
zos.closeEntry();
}
else
{
for(File f: file.listFiles())
{
zipFile(f, relativePath + f.getName() + File.separator, zos);
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if(is != null)
{
is.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
/**
* 解壓縮
* @param fileName
* @param path
*/
public static void unzip(String fileName, String path)
{
FileOutputStream fos = null;
InputStream is = null;
try
{
ZipFile zf = new ZipFile(new File(fileName));
Enumeration en = zf.entries();
while (en.hasMoreElements())
{
ZipEntry zn = (ZipEntry) en.nextElement();
if (!zn.isDirectory())
{
is = zf.getInputStream(zn);
File f = new File(path + zn.getName());
File file = f.getParentFile();
file.mkdirs();
fos = new FileOutputStream(path + zn.getName());
int len = 0;
byte bufer[] = new byte[BUFFERSIZE];
while (-1 != (len = is.read(bufer)))
{
fos.write(bufer, 0, len);
}
fos.close();
}
}
}
catch (ZipException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if(null != is)
{
is.close();
}
if(null != fos)
{
fos.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
/**
* @param args
*/
// public static void main(String[] args)
// {
// //zip(new String[] {"D:/tmp/20150418/logs/file/2015-08-28/debug-log.log","D:/tmp/20150418/logs/file/2015-08-28/error-log.log"}, "D:/tmp/20150418/logs/file/2015-08-28/test.zip");
// //unzip("D://tmep.zip", "D:/temp/");
//
// Map<String, Object> modelMap = new HashMap<>(); //封裝兩個參數作爲一個對象傳入進去
// String templatePagePath = "D://temp/home.vm.html"; //本地靜態頁面地址
// String staticRoot = "D://temp/";
// modelMap.put("staticRoot", staticRoot);
// modelMap.put("courseTypes", null);
// VelocityUtils.staticByTemplate(modelMap,"D://temp/home.vm",templatePagePath); //進行頁面靜態化
// }
}
一個是生成靜態頁面的工具類
package org.leryoo.utils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import java.io.*;
import java.util.Properties;
public class VelocityUtils {
private static Properties p = new Properties();
static {
p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
p.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
p.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
}
/**
* 返回通過模板,將model中的數據替換後的內容
* @param model
* @param templateFilePathAndName
* @return
*/
public static String getContentByTemplate(Object model, String templateFilePathAndName){
try {
Velocity.init(p);
Template template = Velocity.getTemplate(templateFilePathAndName);
VelocityContext context = new VelocityContext();
context.put("model", model);
StringWriter writer = new StringWriter();
template.merge(context, writer);
String retContent = writer.toString();
writer.close();
return retContent;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 根據模板,靜態化model到指定的文件 模板文件中通過訪問model來訪問設置的內容
*
* @param model
* 數據對象
* @param templateFilePathAndName
* 模板文件的物理路徑
* @param targetFilePathAndName
* 目標輸出文件的物理路徑
*/
public static void staticByTemplate(Object model, String templateFilePathAndName, String targetFilePathAndName) {
try {
Velocity.init(p);
Template template = Velocity.getTemplate(templateFilePathAndName);
VelocityContext context = new VelocityContext();
context.put("model", model);
FileOutputStream fos = new FileOutputStream(targetFilePathAndName);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));// 設置寫入的文件編碼,解決中文問題
template.merge(context, writer);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 靜態化內容content到指定的文件
*
* @param content
* @param targetFilePathAndName
*/
public static void staticBySimple(Object content, String targetFilePathAndName) {
VelocityEngine ve = new VelocityEngine();
ve.init(p);
String template = "${content}";
VelocityContext context = new VelocityContext();
context.put("content", content);
StringWriter writer = new StringWriter();
ve.evaluate(context, writer, "", template);
try {
FileWriter fileWriter = new FileWriter(new File(targetFilePathAndName));
fileWriter.write(writer.toString());
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
然後寫一個靜態化的全家桶
Controller層
@Autowired
private IPageConfigService pageConfigService;
@PostMapping("/pageStatic")
AjaxResult staticPage(
@RequestParam(value = "pageName",required = true) String pageName,
@RequestParam(value = "dataKey",required = true)String dataKey){
try {
pageConfigService.staticPage(pageName,dataKey);
return AjaxResult.me();
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.me().setMessage("靜態化失敗!"+e.getMessage());
}
}
Service層 也是整個頁面靜態化的核心
package org.leryoo.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import feign.Response;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.leryoo.client.FastDfsClient;
import org.leryoo.client.RedisClient;
import org.leryoo.domain.PageConfig;
import org.leryoo.domain.Pager;
import org.leryoo.mapper.PageConfigMapper;
import org.leryoo.mapper.PagerMapper;
import org.leryoo.service.IPageConfigService;
import org.leryoo.util.AjaxResult;
import org.leryoo.utils.VelocityUtils;
import org.leryoo.utils.ZipUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import java.io.*;
import java.util.List;
import java.util.Map;
/**
* <p>
* 服務實現類
* </p>
*
* @author yhptest
* @since 2020-02-24
*/
@Service
public class PageConfigServiceImpl extends ServiceImpl<PageConfigMapper, PageConfig> implements IPageConfigService {
@Autowired
private PagerMapper pagerMapper;
@Autowired
private FastDfsClient fastDfsClient;
@Autowired
private RedisClient redisClient;
@Autowired
private PageConfigMapper pageConfigMapper;
//正在做頁面靜態化
@Override //courseTypes- xxx jobTypes=yyy
public void staticPage(String pageName, String dataKey){
InputStream inputStream = null;
FileOutputStream os = null;
try{
//一 通過pageName獲取Page並從中獲取模板 url(zip)+name(home.vm)
//1.1 查詢page對象
List<Pager> pagers = pagerMapper.selectList(new EntityWrapper<Pager>().eq("name", pageName));
if (pagers == null || pagers.size()<1) return;
Pager pager = pagers.get(0);
//1.2 獲取模板zip包,它是fastdfs的地址
String templateUrl = pager.getTemplateUrl();
//1.3 使用fastdfsclient下載它
Response response = fastDfsClient.download(templateUrl);
inputStream = response.body().asInputStream();
String tmpdir=System.getProperty("java.io.tmpdir"); //跨操作系統
String unzipFile = tmpdir+"/temp.zip";
os = new FileOutputStream(unzipFile);
IOUtils.copy(inputStream, os);
System.out.println("下載路徑:" + unzipFile);
//1.4 解壓縮
String unzipDir = tmpdir+"/temp/";
ZipUtil.unzip(unzipFile,unzipDir);
System.out.println("解壓路徑:" + unzipDir);
//1.5 解壓路徑拼接模板名稱得到完整的模板地址
String tempatePath = unzipDir+"/"+pager.getTemplateName(); //c://temp/home.vm
System.out.println("模板路徑:" + tempatePath);
//二 通過dataKey獲取數據
//{"courseTypes":[]}
String jsonStr = redisClient.get(dataKey);
Map model = JSONObject.parseObject(jsonStr,Map.class); //獲取傳遞的map數據
//{"courseTypes":[],"staticRoot":"xxx"}
model.put("staticRoot",unzipDir); // 往map裏面追加參數
//三 做頁面靜態化到本地
String staticPath = tempatePath+".html";//c://temp/home.vm.html
System.out.println("靜態化頁面地址:"+staticPath);
VelocityUtils.staticByTemplate(model,tempatePath,staticPath);
//四 把靜態化好的頁面上傳到fast dfs
AjaxResult ajaxResult = fastDfsClient
.upload(new CommonsMultipartFile(createFileItem(new File(staticPath))));
//五 存放pageConfig
PageConfig pageConfig = new PageConfig();
pageConfig.setTemplateUrl(templateUrl);
pageConfig.setTemplateName(pager.getTemplateName());
pageConfig.setDataKey(dataKey);
pageConfig.setPhysicalPath(pager.getPhysicalPath());
pageConfig.setDfsType(0L); //0表示fastDfs
pageConfig.setPageUrl((String) ajaxResult.getResultObj()); //上傳完的地址
pageConfig.setPageId(pager.getId());
pageConfigMapper.insert(pageConfig);
//@TODO 六 發送Message到mq中
}catch (Exception e){
e.printStackTrace();
}finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
創建FileItem
*/
private FileItem createFileItem(File file) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
String textFieldName = "textField";
FileItem item = factory.createItem("file", "text/plain", true, file.getName());
int bytesRead = 0;
byte[] buffer = new byte[8192];
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = item.getOutputStream();
while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return item;
}
}
hrm-page-client
page客戶端 通過feign完成服務之間的調用 將這個服務暴露出去
導入依賴
<dependency>
<groupId>org.leryoo</groupId>
<artifactId>hrm-page-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--客戶端feign支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
PageConfigClient.java
@FeignClient(value = "HRM-PAGE",fallbackFactory = PageConfigClientFallbackFactory.class )//服務提供
@RequestMapping("/pageConfig")
public interface PageConfigClient {
/**
* 有了pageName就可以獲取模塊
* 有了datakey就可以獲取數據
* @param pageName
* @param dataKey
* @return
*/
@PostMapping("/pageStatic")
AjaxResult staticPage(
@RequestParam(value = "pageName",required = true) String pageName,
@RequestParam(value = "dataKey",required = true)String dataKey);
}
PageConfigClientFallbackFactory.java
@Component
public class PageConfigClientFallbackFactory implements FallbackFactory<PageConfigClient> {
@Override
public PageConfigClient create(Throwable throwable) {
return new PageConfigClient() {
@Override
public AjaxResult staticPage(String pageName, String dataKey) {
return AjaxResult.me().setSuccess(false).setMessage("靜態化失敗!");
}
};
}
}
前端準備
我們可以通過頁面點擊按鈕 或者測試方法生成靜態頁面 這裏我們選擇用頁面點擊按鈕的方式
首先 我們先創建一個頁面 只加一個按鈕
前端代碼
課程中心的代碼
/**
* 頁面靜態化方法
*/
@PostMapping("/staticIndexPageInit")
public AjaxResult staticIndexPageInit()
{
try{
courseTypeService.staticIndexPageInit();
//調用頁面靜態化的邏輯
return AjaxResult.me();
}catch (Exception e){
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage(e.getMessage());
}
}
Service層 通過feign去調用page模塊的方法 完成靜態頁面的生成
@Override
public void staticIndexPageInit() {
//1頁面名稱寫死,約定大於配置
String pageName = "CourseSiteIndex";
//2 需要一個保存到redis數據庫的key
String dataKey="CourseSiteIndex_data";
//本來就是從redis獲取,還要在放一次,其他頁面靜態化的場景不一定有緩衝
List<CourseType> courseTypes = this.treeData(0L);
//課程處理
Map<String,Object> courseTypeSdata = new HashMap<>();
courseTypeSdata.put("courseTypes",courseTypes);
//職位
redisClient.add(dataKey, JSONArray.toJSONString(courseTypeSdata)); //{courseTypes:[]}
//3 調用方法模板+數據=靜態頁面
pageConfigClient.staticPage(pageName,dataKey);
}
然後點擊按鈕 就會在臨時文件夾裏生成
這也就ok了