由于效率的缘故,浏览器通常会缓存js文件,这就给我们带了一个问题:当服务器端部署的项目中的js文件进行了修改后,如果再客户端不手动去刷新一次页面,js的修改效果就不会起作用,因为浏览器还是用的缓存在本地的js文件。
为了解决这个问题,我们采用了这个方案,每当发布新的版本部署到服务器上的时候,我们给html页面中引用的js增加一个新的后缀,形如版本号的东西。
- <script language="javascript" src="view/shell/login.js?version=20081112050245"></script>
为了自动完成这一功能,我们使用了ant,写了一个ant task来完成该项功能。
- /**
- * 该任务遍历web目录,找出所有的模板文件,给js和css的引用加上版本号
- *
- * @version 1.0 2008-07-02
- * @author huangyuanmu
- * @since JDK 1.5.0_8
- */
- public class AddJsAndCssVersionToVm extends Task {
- private String path;
- public void execute() throws BuildException {
- Date date = new Date();
- SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmss");
- String version = df.format(date);
- addVersionToVm(path, version);
- }
- /**
- * 遍历web目录中的vm文件,给js和css的引用加上版本号
- *
- * @author huangyuanmu 2008-07-02
- * @param path
- */
- private void addVersionToVm(String path, String version) {
- File dir = new File(path);
- File[] files = dir.listFiles();
- if (files == null)
- return;
- for (int i = 0; i < files.length; i++) {
- if (files[i].isDirectory()) {
- addVersionToVm(files[i].getAbsolutePath(), version);
- } else {
- String strFileName = files[i].getAbsolutePath().toLowerCase();
- // 如果是符合条件的文件,则添加版本信息
- if (strFileName.endsWith(".vm")
- || strFileName.endsWith(".html")
- || strFileName.endsWith(".jsp")) {
- // RandomAccessFile raf = null;
- InputStream is = null;
- OutputStream os = null;
- List<String> contentList = new ArrayList<String>();
- // 读文件
- try {
- is = new FileInputStream(files[i]);
- Reader r = new InputStreamReader(is);
- BufferedReader br = new BufferedReader(r);
- String line = null;
- while ((line = br.readLine()) != null) {
- String modLine = getModLine(line, version);
- if (modLine != null) {
- line = modLine;
- }
- line = line + "\r\n";
- contentList.add(line);
- }
- // 关闭流
- br.close();
- r.close();
- } catch (Exception e) {
- System.out.println("读文件失败");
- e.printStackTrace();
- } finally {
- if (null != is) {
- try {
- is.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- // 写文件
- try {
- os = new FileOutputStream(files[i]);
- Writer w = new OutputStreamWriter(os);
- BufferedWriter bw = new BufferedWriter(w);
- for (Iterator<String> it = contentList.iterator(); it
- .hasNext();) {
- String line = it.next();
- bw.write(line);
- }
- // 更新到文件
- bw.flush();
- // 关闭流
- bw.close();
- w.close();
- } catch (Exception e) {
- System.out.println("写文件失败");
- e.printStackTrace();
- } finally {
- if (null != os) {
- try {
- os.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
- }
- }
- /**
- * 查找行中是否有js或css的引用,如果有,则加上版本号
- *
- * @author huangyuanmu 2008-07-03
- * @param line
- */
- private String getModLine(String line, String version) {
- // 增加js版本
- line.trim();
- if (line.startsWith("<script") && line.endsWith("</script>")) {
- int pos = line.indexOf(".js");
- String modLine = line.substring(0, pos) + ".js?version="
- + version + "\"></script>";
- return modLine;
- } else if (line.startsWith("<link")
- && line.endsWith("rel=\"stylesheet\" type=\"text/css\">")) {
- int pos = line.indexOf(".css");
- String modLine = line.substring(0, pos) + ".css?version="
- + version
- + "\" rel=\"stylesheet\" type=\"text/css\">";
- return modLine;
- } else {
- return null;
- }
- }
- public void setPath(String path) {
- this.path = path;
- }
- }
当然,这个程序的实现还有一些缺点。首先,模板文件中对js和css的引用必须规范,符合程序代码中描述的格式。另外,不管文件内容有没有改变,都加上了新的版本号,这会用户访问时不必要的网络流量,可能会对页面展现的速度产生一些影响。
ant的build脚本文件在另外一篇中(http://huangyuanmu.iteye.com/admin/blogs/493144)。
注意上面的每个JS文件和CSS文件都加上了一个时间戳作为版本号。
即
<script type=”text/javascript” src=”{JS文件连接地址}?version=XXXXXXXX”></script>
或
<link rel=”stylesheet” type=”text/css” href=”{CSS文件连接地址}?version=XXXXXXXX”>
因为浏览器缓存的时候是以URL作为存储单位(还记得POST页面的返回按钮的问题吧?),
从而当每次首页的文件发生更新的时候只需要更改上面的版本号,就能提醒浏览器重新下载该文件了