`
shinestarwang
  • 浏览: 42509 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

file多浏览器下获取完整文件名(抄袭)

阅读更多
程序说明

【基本原理】

图片预览主要包括两个部分:从file表单控件获取图像数据,根据数据显示预览图像。
程序的file和img属性就是用来保存file控件和显示预览图像的容器的,而img还必须是img元素。

程序有以下几种预览方式:
simple模式:直接从file的value获取图片路径来显示预览,适用于ie6;
filter模式:通过selection获取file的图片路径,再用滤镜来显示预览,适用于ie7/8;
domfile模式:调用file的getAsDataURL方法获取Data URI数据来显示预览,适用于ff3;
remote模式:最后的办法,把file提交后台处理后返回图片数据来显示预览,全适用。

程序定义时就自动根据浏览器设置MODE属性:
ImagePreview.MODE = $$B.ie7 || $$B.ie8 ? "filter" :
    $$B.firefox ? "domfile" :
    $$B.opera || $$B.chrome || $$B.safari ? "remote" : "simple";

如果用能力检测会比较麻烦,所以只用了浏览器检测。
由于浏览器对应的默认模式是不会变的,这个值直接会保存到函数属性中作为公用属性。
ps:ie6也可以用filter模式,不过它有更好的simple模式。


【获取数据】

调用preview方法,就会执行预览程序:
if ( this.file && false !== this.onCheck() ) {
    this._preview( this._getData() );
}

在通过检测后,再调用_getData获取数据,并作为_preview的参数进入预览下一步。

程序初始化时就会根据mode来设置_getData数据获取程序:
this._getData = this._getDataFun(opt.mode);

mode的默认值是ImagePreview.MODE,也可以在可选参数中自定义。
由于兼容性问题,一般应保留默认值,除非是使用全兼容的remote模式。

在_getDataFun里面,根据mode返回数据获取程序:
switch (mode) {
    case "filter" :
        return this._filterData;
    case "domfile" :
        return this._domfileData;
    case "remote" :
        return this._remoteData;
    case "simple" :
    default :
        return this._simpleData;
}

不同的模式有不同的数据获取程序:
滤镜数据获取程序:
this.file.select();
try{
    return document.selection.createRange().text;
} finally { document.selection.empty(); }
一般用在ie7/8,在file控件select后再用selection对象获得文件本地路径。
此时file控件不能隐藏,否则不能被select,不过一般能选择文件就肯定能被select了。
确实要隐藏也可以在获取数据之后再隐藏。

domfile数据获取程序:
return this.file.files[0].getAsDataURL();
用getAsDataURL从file控件获取数据,这个方法暂时只有ff3支持。

远程数据获取程序:
this._setUpload();
this._upload && this._upload.upload();
用_upload上传文件对象把数据提交后台,根据返回的数据再显示。
这个方法不属于本地预览,是没有办法中的办法。

一般数据获取程序:
return this.file.value;
最原始的方法,现在只有ie6还支持从file的value直接获取本地路径。

获取的数据作为参数,在_preview预览程序中进行预览:
if ( !!data && data !== this._data ) {
    this._data = data; this._show();
}

首先排除空值或相同值的情况,再执行_show预览显示程序,其中_data属性用来保存当前的图片数据。
图片使用Data URI数据时可能会设置一个很大的src值,在ie8获取很大的src值会出现“无效指针”的错误。
使用_data属性保存这个值可以避免从src获取值而触发这个错误。

远程数据获取程序没有返回值,因为它需要等待返回数据,在_preview中会自动排除。


【显示预览】

程序初始化时就会根据mode来设置_show预览显示程序:
this._show = opt.mode !== "filter" ? this._simpleShow : this._filterShow;

除了filter模式,都是使用_simpleShow显示程序来显示预览图片的。
里面会先调用_simplePreload方法设置一般预载图片对象:
var preload = this._preload = new Image(), oThis = this;
preload.onload = function(){ oThis._imgShow( oThis._data, this.width, this.height ); };
preload.onerror = function(){ oThis._error(); };

预载图片对象保存在_preload属性中,主要用来判断图像能否加载成功并获取图片原始尺寸。
要实现这些功能只要用Image对象就足够了。
在onload中执行_imgShow显示预览,在onerror中进行出错处理。
ps:ff、chrome和safari的图片对象还有naturalHeight和naturalWidth属性可以获取图片的原始尺寸。

然后设置_preload的src预载图片,如果成功预载就会执行_imgShow显示预览。
要注意src的设置要在onload/onerror的设置之后,否则设置之前就加载完成的话就触发不了事件了。

_imgShow需要三个参数,包括要预览图片的src值,图片原始宽度和图片原始高度。
在_imgShow里面首先设置预览图片的尺寸:
var img = this.img, style = img.style,
    ratio = Math.max( 0, this.ratio ) || Math.min( 1,
            Math.max( 0, this.maxWidth ) / width  || 1,
            Math.max( 0, this.maxHeight ) / height || 1
        );
   
style.width = Math.round( width * ratio ) + "px";
style.height = Math.round( height * ratio ) + "px";

这里的关键是获取ratio比例值,如果自定义的ratio大于0就直接使用自定义的比例,否则就根据参数自动计算。
自动计算首先要确保maxWidth最大宽度和maxHeight最大高度大于等于0。
然后分别跟原始宽高做“/”运算得到比例,如果比例为0表示不限制,那么比例就自动改为1。
最后取比较小的比例来计算,程序设定了比例最大值为1,这样就不会自动放大图片了。
当然比例的计算可以根据需要自行修改。
ps:style的优先级比属性(width/height)高,所以要用style设置。

最后设置img的src就实现预览了。


【remote模式】

remote模式会先提交file控件到后台,通过返回图片数据来显示图片。
它跟其他模式最大的区别就是获取数据的部分。

在_remoteData远程数据获取程序中,会调用_setUpload来设置上传文件对象。
如果设置了action,并存在QuickUpload函数,就会进行实例化一个上传文件对象保存到_upload中:
var oThis = this;
this._upload = new QuickUpload(this.file, {
    onReady: function(){
        this.action = oThis.action; this.timeout = oThis.timeout;
        var parameter = this.parameter;
        parameter.ratio = oThis.ratio;
        parameter.width = oThis.maxWidth;
        parameter.height = oThis.maxHeight;
    },
    onFinish: function(iframe){
        try{
            oThis._preview( iframe.contentWindow.document.body.innerHTML );
        }catch(e){ oThis._error("remote error"); }
    },
    onTimeout: function(){ oThis._error("timeout error"); }
});

这里使用的QuickUpload就是简便无刷新文件上传程序。
在onReady中设置参数,在onFinish中处理返回数据,onTimeout进行出错处理。
返回的数据可以是图片的地址或对应Data URI数据,然后给_preview处理。
当然针对不同的后台输出,数据处理的方式也不同,可以按照需要修改。

后台最好先根据传递的参数缩小图片,尽量减少返回数据来提高预览速度。


【filter模式】

filter模式在_filterData程序中得到文件本地路径,但ie7/8都不允许直接使用本地路径显示图片。
不过还是可以通过滤镜,用本地路径来做预览图片效果。

filter模式使用_filterShow方法来显示预览图片。
里面先调用_filterPreload方法来设置滤镜预载图片对象。
跟一般预载图片对象不同,滤镜预载对象是用滤镜来显示图片,所以并不一定要图像元素。
程序就使用了div元素作为滤镜预载对象:
var preload = this._preload = document.createElement("div");

$$D.setStyle( preload, {
    width: "1px", height: "1px",
    visibility: "hidden", position: "absolute", left: "-9999px", top: "-9999px",
    filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='image')"
});

var body = document.body; body.insertBefore( preload, body.childNodes[0] );

在样式设置中隐藏元素并添加滤镜,要使滤镜生效width和height必须设置一个值。
由于要获取尺寸,只能用visibility来隐藏并插入body,关于AlphaImageLoader滤镜后面再介绍。

然后在_filterShow中预载图片:
try{
    preload.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = data;
}catch(e){ this._error("filter error"); return; }

成功的话,再给img载入图片:
this.img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src='" + data + "')";

最后调用_imgShow设置尺寸:
this._imgShow( ImagePreview.TRANSPARENT, preload.offsetWidth, preload.offsetHeight );

由于img是一个图片对象,默认会显示一个小图标,为了去掉这个小图标,可以让它显示一个透明图片。
程序传递了ImagePreview.TRANSPARENT来设置透明图片,具体数据在Data URI 和 MHTML再说明。
ps:当然也可以在filter模式用div做预览图片对象就没有小图标了,但这样兼容起来会麻烦很多。


【AlphaImageLoader滤镜】

filter模式使用的是AlphaImageLoader滤镜。
它的作用是在对象容器边界内,在对象的背景和内容之间显示一张图片。
如果载入的是png图片,其透明度会被支持,可以用来解决png的兼容问题。
详细参考msdn的AlphaImageLoader Filter和“Microsoft.AlphaImageLoader滤镜讲解”。
它包括三个属性:enabled(滤镜是否激活),sizingMethod(图像显示方式)和src(图像路径)。
程序主要使用后面两个属性。

sizingMethod有三种方式:
crop:剪切图片以适应对象尺寸;
image:默认值。增大或减小对象的尺寸边界以适应图片的尺寸;
scale:缩放图片以适应对象的尺寸边界。

对于预载图片对象_preload,要获取图片的原始尺寸,所以要用image方式。
而预览图片对象img,则要根据设定尺寸显示图片,所以要用scale方式。

而src属性的路径还支持本地路径,是实现filter模式的关键所在。
幸运的是滤镜并没有像file控件那样提高安全性,否则就没办法实现图片本地预览了。


【nsIDOMFile接口】

ff从3.0(或更早)开始,就不能通过value获取file的完整路径,也不支持直接用本地路径显示图片。
不过欣喜的是,它同时也提供了nsIDOMFile接口,能更好地获取文件数据。
在ff的file控件有一个FileList对象,包含了带nsIDOMFile接口的File对象。
ps:FileList对象貌似是一个NodeList对象,但目前只能用第一个,可能是为了将来实现一个file控件选择多个文件的功能预留的。

这个File对象有三个获取文件数据的方法:
getAsText:获取文件的文本数据,可以通过参数设置编码;
getAsDataURL:获取文件的Data URI数据;
getAsBinary:获取文件的二进制数据。
其中getAsDataURL获得的Data URI数据可以用于显示图片,_domfileData中就是用它来获取数据的。

File对象还有支持两个属性:fileName(文件名,不包括路径)和fileSize(文件大小)。
相关具体说明参考mozilla的File和nsIDOMFile。


【Data URI 和 MHTML】

上面已经多次提到Data URI,详细介绍请看秦歌的“Data URI 和 MHTML”。
Data URI的主要作用是以字符代替数据,从而把文件“嵌”在代码里。
除了ie,其他浏览器基本都很好的支持了Data URI。
ie8也有限度地支持,详细参考msdn的data Protocol。

由于opera,safari和chrome需要remote模式的浏览器都支持Data URI,所以程序返回的是Data URI形式的数据。
相比返回路径的方法,返回Data URI不需要创建文件,还少一次HTTP请求。
ps:ie8只支持32k的Data URI数据,在ie8使用时要注意数据大小。

在filter模式需要一个透明图片来去掉img默认显示的小图标,一般的方法需要一个图片文件。
为了“省下”这个文件,可以使用Data URI来做一个1*1的透明图片:
data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==
支持Data URI的情况下,只要把img的src设置为这个值就可以显示一个透明图片了。

虽然ie6/7不支持Data URI,还可以用mhtml来代替。
在ImagePreviewd.js开头有一段注释了的代码:
Content-Type: multipart/related; boundary="_CLOUDGAMER"

--_CLOUDGAMER
Content-Location:blankImage
Content-Transfer-Encoding:base64

R0lGODlhAQABAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw==

这是mhtml记录数据的形式,调用时要按以下格式设置img的src:
mhtml:文件完整路径!blankImage

其中blankImage表示要获取的数据在文件对应的Content-Location。
问题在于如何获得script(js文件)的完整路径(包含http开头的路径)。
首先要在脚本运行时获取,当前运行的script肯定是document.scripts的最后一个:
document.scripts[document.scripts.length - 1]
ps:document.scripts详细参考msdn的scripts Collection,ff不支持,可以用getElementsByTagName("script")兼容。
接着可以利用getAttribute从src获取script的完整路径:
document.scripts[document.scripts.length - 1].getAttribute("src", 4)

ie6/7的getAttribute支持第二个参数,设为4表示返回完整路径的url地址,详细参考msdn的getAttribute Method。

结合Data URI 和 MHTML可以这样得到透明图片数据:
ImagePreview.TRANSPARENT = $$B.ie7 || $$B.ie6 ?
    "mhtml:" + document.scripts[document.scripts.length - 1].getAttribute("src", 4) + "!blankImage" :
    "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";

使用时要注意:
脚本必须单独另存为一个文件,作为mhtml需要的文件路径。
要自动获取完整路径需要用script标签链接文件。


【超空间】

程序还有一个dispose方法用于销毁程序。
包括这几个部分:
_upload上传文件对象:它本身已经有一个dispose方法来销毁程序;
_preload预载图片对象:先清除它的onload/onerror事件再移除元素;
file和img属性:直接设为null,由于不是程序创建的元素,留给使用者来移除。

说到移除元素,顺便说一下超空间(DOM hyperspace),这是从“ppk谈javascript”中看到的。
大概指的是当元素不在dom里面,而js又有关联时,元素并不会消失,而是保存在一个称为“超空间”的地方。
详细参考书的DOM 超空间部分。
书中还说可以根据是否有parentNode来判断元素是否在超空间,但测试以下代码:
<body></body>
<script>
var elm = document.createElement("div");
alert(elm.parentNode);
document.body.removeChild(document.body.appendChild(elm));
alert(elm.parentNode);
</script>

第一次parentNode都是null,没有问题,按理第二次也应该是null,但ie却是一个object。
经测试,这个object的nodeType是11,也就是一个碎片对象(FRAGMENT)。
而且各个被removeChild移除的元素的parentNode都不相同,即会生成不同的碎片对象。
这种情况算不算在“超空间”呢,不过书中也只是说“一般来说”,也不用太考究。

那么用innerHTML清除呢?再测试以下代码:
<body><div id="test"></div></body>
<script>
var elm = document.getElementById("test");
document.body.innerHTML = "";
alert(elm.parentNode);
</script>

结果在ie也是null了,看来removeChild和innerHTML在清除元素时产生了不同的结果。

那个碎片对象貌似没什么用(难道为了保证有parentNode?),那是不是innerHTML就一定比removeChild好呢?
再测试以下代码:
<body>
<style>div{border:1px solid #000; height:20px;}</style>
<span><div id="test1">test1</div></span>
<span><div id="test2">test2</div></span>
</body>
<script>
var div1 = document.getElementById("test1"), parent1 = div1.parentNode;
parent1.removeChild(div1);
alert(div1.tagName + ":" + div1.innerHTML);
parent1.appendChild(div1);

var div2 = document.getElementById("test2"), parent2 = div2.parentNode;
parent2.innerHTML = "";
alert(div2.tagName + ":" + div2.innerHTML);
parent2.appendChild(div2);
</script>

当使用removeChild时,移除元素的结构并没有发生变化,各个浏览器的效果都一样。
而使用innerHTML清除时,其他浏览器的效果跟removeChild一样,但在ie被移除的元素就只剩下一个“外壳”了。

个人推测,ie在使用innerHTML时,被移除的元素会变成一个个单独的元素,失去了彼此的联系。
形象点说就是removeChild是直接掰断树枝,还能继续嫁接使用,而innerHTML是把需要的树叶节点取下来,再把树枝烧掉。
ps:仅仅是推测,谁有官方资料请告诉我。

那么removeChild的好处是移除的元素能再次使用,兼容性好,不好的地方是ie会产生一个没用的碎片对象。
而innerHTML的好处是不会产生多余的碎片对象,方便高效,但在ie被移除的元素基本不能再用,有兼容性问题。
那就可以根据需要使用不同的方法了,至于防止内存泄漏用那个好,感觉是innerHTML,但没有更深入研究的话还说不清楚。


使用技巧

一般来preview方法都是在onchange中调用,即选择文件后立即显示预览。

在不需要程序时最好执行一次dispose方法来销毁程序,防止内存泄漏等。

利用ImagePreview.TRANSPARENT可以显示透明图片,而不需另外隐藏或增加文件。

第二个实例中的ResetFile是用来重置file控件的,详细参考这里file的reset。
而file控件样式设置详细参考这里的file样式。

asp版本使用Persits.Jpeg组件缩放图片,测试请先安装该组件。


使用说明

实例化时,有两个必要参数,分别是file控件对象和img元素的预览显示对象:
new ImagePreview( file, img );

可选参数用来设置系统的默认属性,包括:
属性:    默认值//说明
mode:  ImagePreview.MODE,//预览模式
ratio:  0,//自定义比例
maxWidth: 0,//缩略图宽度
maxHeight: 0,//缩略图高度
onCheck: function(){},//预览检测时执行
onShow:  function(){},//预览图片时执行
onErr:  function(){},//预览错误时执行
以下在remote模式时有效
action:  undefined,//设置action
timeout: 0//设置超时(0为不设置)
如果要使用remote模式必须设置一个action。

还提供了以下方法:
preview:执行预览操作;
dispose:销毁程序。
分享到:
评论

相关推荐

    纯HTML代码块导出Excel文件,用jquery库(兼容所有浏览器,支持设置文件名)

    本教程将探讨如何利用jQuery库实现跨浏览器的Excel文件导出,包括设置自定义文件名。 首先,我们需要理解HTML和jQuery的基本概念。HTML是超文本标记语言,用于构建网页结构,而jQuery是一个强大的JavaScript库,它...

    C# 获取大量文件名

    例如,你可以使用以下代码获取当前目录下的所有文件名: ```csharp string[] files = Directory.GetFiles(@"C:\MyDirectory"); foreach (string file in files) { Console.WriteLine(file); } ``` 2. 搜索模式: ...

    获取所有指定目录下所有文件名

    标题 "获取所有指定目录下所有文件名" 描述了一个实用的VBA(Visual Basic for Applications)编程任务,即在Access数据库应用中编写一个程序来枚举并列出特定目录下的所有文件名。VBA是Microsoft Office套件中的一...

    获取指定目录下的所有文件路径及文件名

    "获取指定目录下的所有文件路径及文件名"这一主题是其中的核心部分,它涉及到文件系统接口的使用,以及如何遍历目录结构来收集信息。在这个场景下,我们将深入探讨以下几个关键知识点: 1. **文件路径与文件名**: ...

    字体名称 获取 字体文件名

    本文将详细阐述如何在VS2008 C++环境下,利用Win32 API来获取系统中的所有字体,并根据字体名称找到对应的字体文件名。 首先,我们需要理解Windows操作系统是如何管理字体的。在Windows系统中,字体通常存储在`C:\...

    webClient获取下载文件名示例(不在地址中获取)

    "webClient获取下载文件名示例(不在地址中获取)"这个主题关注的是如何在不依赖URL来获取下载文件的真实名称,而是通过其他方式来实现。这是因为URL中的文件名可能并不是服务器实际发送的文件名,或者为了安全和...

    C# 获取系统字体名 和 对应字体的文件名

    在C#编程中,获取系统字体名及其对应的字体文件名是一项常见的任务,特别是在涉及到文本渲染、界面设计或者自定义字体管理的场景下。系统中的字体是操作系统提供的资源,可以通过编程接口来访问这些信息。以下是对这...

    获取上传文件的文件名

    js获取file标签上传文件的文件名,可以获取文件名判断文件是否重复 以及对文件上传做控制

    获取指定目录下的文件路径及文件名

    在IT行业中,尤其是在编程领域,经常需要处理文件和目录的操作,比如获取指定目录下的文件路径、文件名以及统计文件的数量。这些操作对于文件管理、数据处理、自动化脚本编写等任务至关重要。以下将详细讲解如何实现...

    VC获取当前程序文件的路径,文件名以及路径+文件名

    VC 获取当前程序文件的路径、文件名以及路径+文件名 在 VC++ 中,获取当前程序文件的路径、文件名以及路径+文件名是一个非常常见的问题,本文将为大家介绍两种经典的解决方案。 方法 1:使用 ...

    file-extension:获取给定文件名或路径的扩展名

    文件扩展名 获取给定文件名或路径的扩展名与 : 将点文件视为扩展名( .eslintrc =&gt; eslintrc ) 将无扩展名文件视为扩展名( Makefile =&gt; makefile ) 扩展名中不包含点( package.json =&gt; json ) 默认情况下返回...

    批量获取文件名_批量获取文件名_

    在IT行业中,批量获取文件名是一项常见的操作,尤其在数据处理、文件管理或者自动化脚本编写时显得尤为重要。批量获取文件名的能力可以帮助我们快速列举、分析或者操作大量文件,节省了手动操作的时间和精力。下面将...

    MFC获取磁盘文件名

    本文将深入探讨如何使用MFC获取磁盘上的文件名,同时也会涉及C语言函数和Windows API的使用。 首先,让我们理解基本概念。MFC是微软对C++的封装,它提供了一套面向对象的类库,用于构建基于Windows的应用程序。在...

    C#取得当前文件夹下全部文件路径或文件名.rar

    在C#编程中,获取当前文件夹下所有文件的路径或文件名是一项常见的任务,尤其在文件管理和系统操作相关的应用中。在这个场景中,我们使用VS 2008作为开发环境,通过C#语言来实现这个功能。下面将详细解释如何实现这...

    一键获取目录下所有文件的文件名

    -File | Select-Object Name | Out-File -FilePath filename.txt`命令可以达到相同的效果,它通过`Get-ChildItem`获取当前目录下的文件,`Select-Object Name`选择文件名,最后`Out-File`将结果写入文件。...

    025获取多层文件夹下文件名并创建超链接共2页.pdf.z

    标题中的"025获取多层文件夹下文件名并创建超链接共2页.pdf.z"和描述中的"025获取多层文件夹下文件名并创建超链接共2页.pdf.zip"提示我们,这个内容可能涉及的是一个教程或者指南,教用户如何在多层文件夹结构中获取...

    asp获取当前文件名的代码

    总的来说,通过这段简单的代码,开发者能够方便地在ASP环境中获取当前页面的文件名,从而实现更多功能。这在处理动态页面或者需要根据当前页面进行不同操作的场景中非常实用。在实际编程时,理解并灵活运用这些基础...

    javascript获取浏览器临时目录

    ### JavaScript 获取浏览器...尽管JavaScript直接获取浏览器临时目录的能力有限,但在某些特定环境下(如旧版IE浏览器),通过上述方法仍然可以实现这一功能。对于现代Web开发而言,推荐使用更安全的数据存储解决方案。

    下载过程的文件名生成规则

    在获取这些协议下的文件名时,一般遵循以下步骤: 1. **HTTP/FTP URL 示例**: - FTP: `ftp://iso:mydigit.cn@mydigit.cn/TonPE_V1.4.rar` - HTTP: `http://down.rnovel.com/booktxt/3/13682/13682.zip` 2. **...

    获取指定目录下的文件列表 获取指定目录下的所有文件名和路径(包括子目录)

    在编程领域,尤其是在文件系统操作相关的任务中,获取指定目录下的所有文件名和路径是一项基本且重要的工作。这个过程通常涉及到文件系统遍历,能够帮助开发者处理文件、组织数据或者进行备份等操作。本篇文章将深入...

Global site tag (gtag.js) - Google Analytics