文档章节

浏览器保存网页的四种方式

SuShine
 SuShine
发布于 2015/06/25 15:27
字数 1507
阅读 17
收藏 1
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://mikewang.blog.51cto.com/3826268/1206701

当我们使用浏览器浏览网页时,常常想保存内容,目的可能是离线阅读或者是收藏。之前的一个项目用到一些,一并总结。


方式一,Snapshot

4.0支持此方法saveViewState(),方法源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
      * Saves the view data to the output stream. The output is highly
      * version specific, and may not be able to be loaded by newer versions
      * of WebView.
      * @param stream The {@link OutputStream} to save to
      * @return True if saved successfully
      * @hide
      */
     public  boolean  saveViewState(OutputStream stream) {
         try  {
             return  ViewStateSerializer.serializeViewState(stream,  this );
         catch  (IOException e) {
             Log.w(LOGTAG,  "Failed to saveViewState" , e);
         }
         return  false ;
     }

4.0原生浏览器其实是把网页的内容存入到BLOB中,缺陷很明显,网页一大就保存不了,查了下源码,BLOB保存的数据有大小限制。4.0上,超过2M大小的网页就没法保存,源码截图如下:




但是4.1上,saveViewState() 更新了参数,增加了回调。方法源码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
      * Saves the view data to the output stream. The output is highly
      * version specific, and may not be able to be loaded by newer versions
      * of WebView.
      * @param stream The {@link OutputStream} to save to
      * @param callback The {@link ValueCallback} to call with the result
      */
     public  void  saveViewState(OutputStream stream, ValueCallback<Boolean> callback) {
         if  (mWebViewCore ==  null ) {
             callback.onReceiveValue( false );
             return ;
         }
         mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SAVE_VIEW_STATE,
                 new  WebViewCore.SaveViewStateRequest(stream, callback));
     }

4.1原生浏览器去snapshot的处理更好了,明白了这个限制之后,网页的data不存数据库了,改存文件了,所以网页太大不能保存的问题就解决了。

考虑到大部分的网页都比较小的情况下,且实现上快速,采用4.0的方法。

主代码采用反射调用4.0 的saveViewState()方法,方法名改为mySaveViewState(),代码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  boolean  mySaveViewState(OutputStream stream) {
         boolean  ret =  false ;
         if  (mMethodSaveViewState ==  null )
             try  {
                 mMethodSaveViewState = WebView. class .getDeclaredMethod(
                         "saveViewState" , OutputStream. class );
                 mMethodSaveViewState.setAccessible( true );
             catch  (NoSuchMethodException e) {
             }
         if  (mMethodSaveViewState !=  null ) {
             try  {
                 ret = (Boolean) mMethodSaveViewState.invoke( this , stream);
             catch  (IllegalArgumentException e) {
                 e.printStackTrace();
             catch  (IllegalAccessException e) {
                 e.printStackTrace();
             catch  (InvocationTargetException e) {
                 e.printStackTrace();
             }
         }
         return  ret;
     }


方式二,Stream(file)

因为2.3上不支持saveViewState()方法,所以采用SavePicture(),SavePicture()方法其实来自SaveState(),在webview前进后退后者其他操作时,会保存相应的状态。

方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
     * Save the state of this WebView used in
     * {@link android.app.Activity#onSaveInstanceState}. Please note that this
     * method no longer stores the display data for this WebView. The previous
     * behavior could potentially leak files if {@link #restoreState} was never
     * called. See {@link #savePicture} and {@link #restorePicture} for saving
     * and restoring the display data.
     * @param outState The Bundle to store the WebView state.
     * @return The same copy of the back/forward list used to save the state. If
     *         saveState fails, the returned list will be null.
     * @see #savePicture
     * @see #restorePicture
     */
    public  WebBackForwardList saveState(Bundle outState) {
        if  (outState ==  null ) {
            return  null ;
        }
        // We grab a copy of the back/forward list because a client of WebView
        // may have invalidated the history list by calling clearHistory.
        WebBackForwardList list = copyBackForwardList();
        final  int  currentIndex = list.getCurrentIndex();
        final  int  size = list.getSize();
        // We should fail saving the state if the list is empty or the index is
        // not in a valid range.
        if  (currentIndex <  0  || currentIndex >= size || size ==  0 ) {
            return  null ;
        }
        outState.putInt( "index" , currentIndex);
        // FIXME: This should just be a byte[][] instead of ArrayList but
        // Parcel.java does not have the code to handle multi-dimensional
        // arrays.
        ArrayList< byte []> history =  new  ArrayList< byte []>(size);
        for  ( int  i =  0 ; i < size; i++) {
            WebHistoryItem item = list.getItemAtIndex(i);
            if  ( null  == item) {
                // FIXME: this shouldn't happen
                // need to determine how item got set to null
                Log.w(LOGTAG,  "saveState: Unexpected null history item." );
                return  null ;
            }
            byte [] data = item.getFlattenedData();
            if  (data ==  null ) {
                // It would be very odd to not have any data for a given history
                // item. And we will fail to rebuild the history list without
                // flattened data.
                return  null ;
            }
            history.add(data);
        }
        outState.putSerializable( "history" , history);
        if  (mCertificate !=  null ) {
            outState.putBundle( "certificate" ,
                               SslCertificate.saveState(mCertificate));
        }
        return  list;
    }
    /**
     * Save the current display data to the Bundle given. Used in conjunction
     * with {@link #saveState}.
     * @param b A Bundle to store the display data.
     * @param dest The file to store the serialized picture data. Will be
     *             overwritten with this WebView's picture data.
     * @return True if the picture was successfully saved.
     */
    public  boolean  savePicture(Bundle b,  final  File dest) {
        if  (dest ==  null  || b ==  null ) {
            return  false ;
        }
        final  Picture p = capturePicture();
        // Use a temporary file while writing to ensure the destination file
        // contains valid data.
        final  File temp =  new  File(dest.getPath() +  ".writing" );
        new  Thread( new  Runnable() {
            public  void  run() {
                FileOutputStream out =  null ;
                try  {
                    out =  new  FileOutputStream(temp);
                    p.writeToStream(out);
                    // Writing the picture succeeded, rename the temporary file
                    // to the destination.
                    temp.renameTo(dest);
                catch  (Exception e) {
                    // too late to do anything about it.
                finally  {
                    if  (out !=  null ) {
                        try  {
                            out.close();
                        catch  (Exception e) {
                            // Can't do anything about that
                        }
                    }
                    temp.delete();
                }
            }
        }).start();
        // now update the bundle
        b.putInt( "scrollX" , mScrollX);
        b.putInt( "scrollY" , mScrollY);
        b.putFloat( "scale" , mActualScale);
        b.putFloat( "textwrapScale" , mTextWrapScale);
        b.putBoolean( "overview" , mInZoomOverview);
        return  true ;
    }

具体实现上,我们需要传入一个bundle,保存相应的状态。


方式三: png

保存成图片的缺陷很大,放缩的失真,不能编辑。Webview有capturePicture()方法支持。源码中此方法的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
     * Return a new picture that captures the current display of the webview.
     * This is a copy of the display, and will be unaffected if the webview
     * later loads a different URL.
     *
     * @return a picture containing the current contents of the view. Note this
     *         picture is of the entire document, and is not restricted to the
     *         bounds of the view.
     */
    public  Picture capturePicture() {
        if  ( null  == mWebViewCore)  return  null // check for out of memory tab
        return  mWebViewCore.copyContentPicture();
    }

传入picture,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//FIXME draw on canvas can not run in a non-ui thread
         boolean  saveAsPng(Context ctx, ContentValues values, Picture picture){
             String path = UUID.randomUUID().toString();
             OutputStream outs =  null ;
             try  {
                 if  ( null  == picture)
                     return  false ;
                 String png = path +  ".png" ;
                 outs = ctx.openFileOutput(png, Context.MODE_PRIVATE);
                 PictureDrawable pictureDrawable =  new  PictureDrawable(
                         picture);
                 Bitmap bitmap =  null ;
                 bitmap = Bitmap.createBitmap(
                         pictureDrawable.getIntrinsicWidth(),
                         pictureDrawable.getIntrinsicHeight(),
                         Config.ARGB_8888);
                 Canvas canvas =  new  Canvas(bitmap);
                 canvas.drawPicture(pictureDrawable.getPicture());
                 bitmap.compress(Bitmap.CompressFormat.PNG,  100 , outs);
                 outs.flush();
                 outs.close();
                 bitmap.recycle();
                 bitmap =  null ;
                 values.put(SavedPageColumns.TYPE, SavedPage.TYPE_PNG);
                 values.put(SavedPageColumns.PATH, png);
             catch  (Exception e) {
                 Log.w(LOGTAG,  "Failed to save page " , e);
                 if  (outs !=  null ) {
                     try  {
                         outs.close();
                     catch  (IOException ignore) {
                     }
                 }
                 File file = ctx.getFileStreamPath(path);
                 if  (file !=  null  && file.exists() && !file.delete()) {
                     file.deleteOnExit();
                 }
                 return  false ;
             }
             return  true ;
         }


方法四: 保存html网页

直接将url给下载管理器即可。

本文出自 “小新专栏” 博客,请务必保留此出处http://mikewang.blog.51cto.com/3826268/1206701

本文转载自:http://blog.csdn.net/sfshine/article/details/17955487

共有 人打赏支持
SuShine
粉丝 123
博文 496
码字总数 144699
作品 0
朝阳
后端工程师
多内核IE浏览器--IETester

在设计网页的时候,不得不考虑浏览器兼容问题,就IE在市场上就有好几个版本在使用,而每个版本对CSS的解析方式都会有些许的差别,要做到兼容各 种主流浏览器,我们首先要做的当然是安装各种不...

匿名
2009/12/21
14.1K
0
IETester 7.3.3 发布,多版本 IE 浏览器

IETester 7.3.3 发布,该版本修复了在 Win7+ 系统上从 DebugBar 菜单中启动 IETester 时出现“IETester not found” 的问题。 在设计网页的时候,不得不考虑浏览器兼容问题,就IE在市场上就有...

oschina
2014/08/24
3.9K
18
MindManager创建链接和改变分支主题的间距

Mindmanager是一款非常好用的思维导图软件,用户只需要花上一点时间就能掌握其使用方法,现在就让我来和大家分享一下如何在mindmanager中创建链接和改变分支主题的间距。 创建链接: 链接功能...

neluzyy1
2015/03/16
0
0
Skype for business/Lync之证书解析(四)证书申请的四种方式

有四种方式进行证书申请,这四种方式不仅适合skype/lync,也适合任何证书申请场景(除了第一种方式): 第一种方式:在SFB/LYNC安装界面中用证书向导自动生成与分配证书,操作最简单,但生成...

ultradream
2015/08/27
0
0
js的offset,client,scroll的区别

javascript的offset、client、scroll在使用过程中非常频繁,接下来将对此进行一一介绍,了解其区别和使用方法。 offsetTop 指元素距离上方或上层控件的位置,整型,单位像素。 offsetLeft 指...

山鹰sniper
2014/11/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Mac OS X下Maven的安装与配置

Mac OS X 安装Maven: 下载 Maven, 并解压到某个目录。例如/Users/robbie/apache-maven-3.3.3 打开Terminal,输入以下命令,设置Maven classpath $ vi ~/.bash_profile 添加下列两行代码,之后...

TonyStarkSir
今天
3
0
关于编程,你的练习是不是有效的?

最近由于工作及Solution项目的影响,我在重新学习DDD和领域建模的一些知识。然后,我突然就想到了这个问题,以及我是怎么做的? 对于我来说,提升技能的项目会有四种: 纯兴趣驱动的项目。即...

问题终结者
今天
3
0
打开eclipse出现an error has occurred see the log file

解决方法: 1,打开eclipse安装目录下的eclipse.ini文件; 2,打开的文本文件最后添加一行 --add-modules=ALL-SYSTEM 3,保存重新打开Eclipse。...

任梁荣
昨天
4
0
搞定Northwind示例数据库,无论哪个版本的SQLServer都受用

Northwind数据库 从这里可以找到突破口: http://social.msdn.microsoft.com/Forums/zh-CN/Vsexpressvb/thread/8490a1c6-9018-40c9-aafb-df9f79d29cde 下面是MSDN: http://msdn2.microsoft......

QQZZFT
昨天
1
0
mysql主从同步,安装配置操作

准备 两台mysql服务,我这里准备了如下: 主库:192.168.176.128 从库:192.168.176.131 如何在Linux上安装mysql服务,请看https://blog.csdn.net/qq_18860653/article/details/80250499 操作...

小致dad
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部