js实现浏览与保存本地常见文件 2022.10.24
1、需求分析
在平时的学习和工作中,相信大家会经常遇到这样一个问题:我在阅读网页时遇到一个json文件,保存到本地后需要查看,那么我该选择什么样的IDE来打开这些文件呢?。
此时,为了解决上述问题,一般来说大致有三类解决方案:第一种
,其中最简单的方式是用记事本Notepad
打开,但可能有的小伙伴不喜欢使用Window操作系统自带的记事本查看文本文件,因为没有特定的颜色和变量标识,不利于编码;第二种方式,可以安装一些 IDE文本编辑器
来打开文件进行编辑;第三种,同时也是我认为较好的方方法,尤其对于比较懒的一些工程师或者开发者而言,浏览器Browser
往往是最快捷、最普遍、最简单的文件查看器。
2、使用js方法介绍
为了编写JavaScript
代码来加载和保存本地文件,我们需要进行文件的读取操作,即文件读取和文件写入,具体涉及到的方法就是FileSaver
和FileReader
。
2.1 FileSaver.js 简介
FileSaver是一种在客户端保存文件的解决方案,并且具有非常完美的应用表现,尤其是客户端Web应用的文件生成方面。它支持的浏览器包括Firefox、Chrome、Edge、IE、Opera 和 Safari
。
|
FileSaver.js
文件内容
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define([], factory);
} else if (typeof exports !== "undefined") {
factory();
} else {
var mod = {
exports: {}
};
factory();
global.FileSaver = mod.exports;
}
})(this, function () {
"use strict";
/*
* FileSaver.js
* A saveAs() FileSaver implementation.
*
* By Eli Grey, http://eligrey.com
*
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
* source : http://purl.eligrey.com/github/FileSaver.js
*/
// The one and only way of getting global scope in all environments
// https://stackoverflow.com/q/3277182/1008999
var _global = typeof window === 'object' && window.window === window ? window : typeof self === 'object' && self.self === self ? self : typeof global === 'object' && global.global === global ? global : void 0;
function bom(blob, opts) {
if (typeof opts === 'undefined') opts = {
autoBom: false
};else if (typeof opts !== 'object') {
console.warn('Deprecated: Expected third argument to be a object');
opts = {
autoBom: !opts
};
} // prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {
type: blob.type
});
}
return blob;
}
function download(url, name, opts) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onload = function () {
saveAs(xhr.response, name, opts);
};
xhr.onerror = function () {
console.error('could not download file');
};
xhr.send();
}
function corsEnabled(url) {
var xhr = new XMLHttpRequest(); // use sync to avoid popup blocker
xhr.open('HEAD', url, false);
try {
xhr.send();
} catch (e) {}
return xhr.status >= 200 && xhr.status <= 299;
} // `a.click()` doesn't work for all browsers (#465)
function click(node) {
try {
node.dispatchEvent(new MouseEvent('click'));
} catch (e) {
var evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
node.dispatchEvent(evt);
}
} // Detect WebView inside a native macOS app by ruling out all browsers
// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
var isMacOSWebView = /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
var saveAs = _global.saveAs || ( // probably in some web worker
typeof window !== 'object' || window !== _global ? function saveAs() {}
/* noop */
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
: 'download' in HTMLAnchorElement.prototype && !isMacOSWebView ? function saveAs(blob, name, opts) {
var URL = _global.URL || _global.webkitURL;
var a = document.createElement('a');
name = name || blob.name || 'download';
a.download = name;
a.rel = 'noopener'; // tabnabbing
// TODO: detect chrome extensions & packaged apps
// a.target = '_blank'
if (typeof blob === 'string') {
// Support regular links
a.href = blob;
if (a.origin !== location.origin) {
corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank');
} else {
click(a);
}
} else {
// Support blobs
a.href = URL.createObjectURL(blob);
setTimeout(function () {
URL.revokeObjectURL(a.href);
}, 4E4); // 40s
setTimeout(function () {
click(a);
}, 0);
}
} // Use msSaveOrOpenBlob as a second approach
: 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) {
name = name || blob.name || 'download';
if (typeof blob === 'string') {
if (corsEnabled(blob)) {
download(blob, name, opts);
} else {
var a = document.createElement('a');
a.href = blob;
a.target = '_blank';
setTimeout(function () {
click(a);
});
}
} else {
navigator.msSaveOrOpenBlob(bom(blob, opts), name);
}
} // Fallback to using FileReader and a popup
: function saveAs(blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open('', '_blank');
if (popup) {
popup.document.title = popup.document.body.innerText = 'downloading...';
}
if (typeof blob === 'string') return download(blob, name, opts);
var force = blob.type === 'application/octet-stream';
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== 'undefined') {
// Safari doesn't allow downloading of blob URLs
var reader = new FileReader();
reader.onloadend = function () {
var url = reader.result;
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
if (popup) popup.location.href = url;else location = url;
popup = null; // reverse-tabnabbing #460
};
reader.readAsDataURL(blob);
} else {
var URL = _global.URL || _global.webkitURL;
var url = URL.createObjectURL(blob);
if (popup) popup.location = url;else location.href = url;
popup = null; // reverse-tabnabbing #460
setTimeout(function () {
URL.revokeObjectURL(url);
}, 4E4); // 40s
}
});
_global.saveAs = saveAs.saveAs = saveAs;
if (typeof module !== 'undefined') {
module.exports = saveAs;
}
});
2.1.1 FileSaver的import引入(Node环境)
在Vue
等大型项目当中,一般通过npm
安装FileSaver
,输入命令npm install file-saver --save,推荐使用import方式导入js类库,然后进行相关方法的调用,具体如下:
import { saveAs } from 'file-saver';
2.1.2 FileSaver的script引入(HTML文件)
如果要在html
文档中使用FileSaver
库,那么仅仅在script
标签中引入对应的js文件即可,具体如下:
<script src="https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.js"></script>
2.1.3 FileSaver的调用方法
var file = new File(["Hello, world!"], "hello world.txt", {type: "text/plain;charset=utf-8"});
FileSaver.saveAs(file);
// 保存txt文件
var blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
FileSaver.saveAs(blob, "hello world.txt");
//保存 URL
FileSaver.saveAs("https://httpbin.org/image", "image.jpg");
//保存 canvas
var canvas = document.getElementById("my-canvas");
canvas.toBlob(function(blob) {
saveAs(blob, "pretty image.png");
});
当然,如果想要保存网络文件,可以参考Saving a remote file。
2.2 FileReader
这里使用FileReader来对本地磁盘文件进行读取操作,
2.2.1 检测浏览器是否支持FileReader的js函数
function detect_FileReader() {
if (window.FileReader) {
var fr = new FileReader();
return true;
} else {
return false;
}
}
2.2.2 Blob.js
下面放置了Blob.js
文件的代码,里面介绍了Blob
、File
及FileReader的
相关构造器实现,可供感兴趣的开发者参考查看,受益匪浅。
Blob.js
文件内容:
/* Blob.js
* A Blob, File, FileReader & URL implementation.
* 2019-04-19
*
* By Eli Grey, http://eligrey.com
* By Jimmy Wärting, https://github.com/jimmywarting
* License: MIT
* See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md
*/
;(function () {
var global = typeof window === 'object'
? window : typeof self === 'object'
? self : this
var BlobBuilder = global.BlobBuilder
|| global.WebKitBlobBuilder
|| global.MSBlobBuilder
|| global.MozBlobBuilder
global.URL = global.URL || global.webkitURL || function (href, a) {
a = document.createElement('a')
a.href = href
return a
}
var origBlob = global.Blob
var createObjectURL = URL.createObjectURL
var revokeObjectURL = URL.revokeObjectURL
var strTag = global.Symbol && global.Symbol.toStringTag
var blobSupported = false
var blobSupportsArrayBufferView = false
var arrayBufferSupported = !!global.ArrayBuffer
var blobBuilderSupported = BlobBuilder
&& BlobBuilder.prototype.append
&& BlobBuilder.prototype.getBlob
try {
// Check if Blob constructor is supported
blobSupported = new Blob(['ä']).size === 2
// Check if Blob constructor supports ArrayBufferViews
// Fails in Safari 6, so we need to map to ArrayBuffers there.
blobSupportsArrayBufferView = new Blob([new Uint8Array([1, 2])]).size === 2
} catch (e) {}
/**
* Helper function that maps ArrayBufferViews to ArrayBuffers
* Used by BlobBuilder constructor and old browsers that didn't
* support it in the Blob constructor.
*/
function mapArrayBufferViews (ary) {
return ary.map(function (chunk) {
if (chunk.buffer instanceof ArrayBuffer) {
var buf = chunk.buffer
// if this is a subarray, make a copy so we only
// include the subarray region from the underlying buffer
if (chunk.byteLength !== buf.byteLength) {
var copy = new Uint8Array(chunk.byteLength)
copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength))
buf = copy.buffer
}
return buf
}
return chunk
})
}
function BlobBuilderConstructor (ary, options) {
options = options || {}
var bb = new BlobBuilder()
mapArrayBufferViews(ary).forEach(function (part) {
bb.append(part)
})
return options.type ? bb.getBlob(options.type) : bb.getBlob()
}
function BlobConstructor (ary, options) {
return new origBlob(mapArrayBufferViews(ary), options || {})
}
if (global.Blob) {
BlobBuilderConstructor.prototype = Blob.prototype
BlobConstructor.prototype = Blob.prototype
}
/********************************************************/
/* String Encoder fallback */
/********************************************************/
function stringEncode (string) {
var pos = 0
var len = string.length
var Arr = global.Uint8Array || Array // Use byte array when possible
var at = 0 // output position
var tlen = Math.max(32, len + (len >> 1) + 7) // 1.5x size
var target = new Arr((tlen >> 3) << 3) // ... but at 8 byte offset
while (pos < len) {
var value = string.charCodeAt(pos++)
if (value >= 0xd800 && value <= 0xdbff) {
// high surrogate
if (pos < len) {
var extra = string.charCodeAt(pos)
if ((extra & 0xfc00) === 0xdc00) {
++pos
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000
}
}
if (value >= 0xd800 && value <= 0xdbff) {
continue // drop lone surrogate
}
}
// expand the buffer if we couldn't write 4 bytes
if (at + 4 > target.length) {
tlen += 8 // minimum extra
tlen *= (1.0 + (pos / string.length) * 2) // take 2x the remaining
tlen = (tlen >> 3) << 3 // 8 byte offset
var update = new Uint8Array(tlen)
update.set(target)
target = update
}
if ((value & 0xffffff80) === 0) { // 1-byte
target[at++] = value // ASCII
continue
} else if ((value & 0xfffff800) === 0) { // 2-byte
target[at++] = ((value >> 6) & 0x1f) | 0xc0
} else if ((value & 0xffff0000) === 0) { // 3-byte
target[at++] = ((value >> 12) & 0x0f) | 0xe0
target[at++] = ((value >> 6) & 0x3f) | 0x80
} else if ((value & 0xffe00000) === 0) { // 4-byte
target[at++] = ((value >> 18) & 0x07) | 0xf0
target[at++] = ((value >> 12) & 0x3f) | 0x80
target[at++] = ((value >> 6) & 0x3f) | 0x80
} else {
// FIXME: do we care
continue
}
target[at++] = (value & 0x3f) | 0x80
}
return target.slice(0, at)
}
/********************************************************/
/* String Decoder fallback */
/********************************************************/
function stringDecode (buf) {
var end = buf.length
var res = []
var i = 0
while (i < end) {
var firstByte = buf[i]
var codePoint = null
var bytesPerSequence = (firstByte > 0xEF) ? 4
: (firstByte > 0xDF) ? 3
: (firstByte > 0xBF) ? 2
: 1
if (i + bytesPerSequence <= end) {
var secondByte, thirdByte, fourthByte, tempCodePoint
switch (bytesPerSequence) {
case 1:
if (firstByte < 0x80) {
codePoint = firstByte
}
break
case 2:
secondByte = buf[i + 1]
if ((secondByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
if (tempCodePoint > 0x7F) {
codePoint = tempCodePoint
}
}
break
case 3:
secondByte = buf[i + 1]
thirdByte = buf[i + 2]
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
codePoint = tempCodePoint
}
}
break
case 4:
secondByte = buf[i + 1]
thirdByte = buf[i + 2]
fourthByte = buf[i + 3]
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
codePoint = tempCodePoint
}
}
}
}
if (codePoint === null) {
// we did not generate a valid codePoint so insert a
// replacement char (U+FFFD) and advance only 1 byte
codePoint = 0xFFFD
bytesPerSequence = 1
} else if (codePoint > 0xFFFF) {
// encode to utf16 (surrogate pair dance)
codePoint -= 0x10000
res.push(codePoint >>> 10 & 0x3FF | 0xD800)
codePoint = 0xDC00 | codePoint & 0x3FF
}
res.push(codePoint)
i += bytesPerSequence
}
var len = res.length
var str = ''
var i = 0
while (i < len) {
str += String.fromCharCode.apply(String, res.slice(i, i += 0x1000))
}
return str
}
// string -> buffer
var textEncode = typeof TextEncoder === 'function'
? TextEncoder.prototype.encode.bind(new TextEncoder())
: stringEncode
// buffer -> string
var textDecode = typeof TextDecoder === 'function'
? TextDecoder.prototype.decode.bind(new TextDecoder())
: stringDecode
function FakeBlobBuilder () {
function isDataView (obj) {
return obj && DataView.prototype.isPrototypeOf(obj)
}
function bufferClone (buf) {
var view = new Array(buf.byteLength)
var array = new Uint8Array(buf)
var i = view.length
while (i--) {
view[i] = array[i]
}
return view
}
function array2base64 (input) {
var byteToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
var output = []
for (var i = 0; i < input.length; i += 3) {
var byte1 = input[i]
var haveByte2 = i + 1 < input.length
var byte2 = haveByte2 ? input[i + 1] : 0
var haveByte3 = i + 2 < input.length
var byte3 = haveByte3 ? input[i + 2] : 0
var outByte1 = byte1 >> 2
var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4)
var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6)
var outByte4 = byte3 & 0x3F
if (!haveByte3) {
outByte4 = 64
if (!haveByte2) {
outByte3 = 64
}
}
output.push(
byteToCharMap[outByte1], byteToCharMap[outByte2],
byteToCharMap[outByte3], byteToCharMap[outByte4]
)
}
return output.join('')
}
var create = Object.create || function (a) {
function c () {}
c.prototype = a
return new c()
}
if (arrayBufferSupported) {
var viewClasses = [
'[object Int8Array]',
'[object Uint8Array]',
'[object Uint8ClampedArray]',
'[object Int16Array]',
'[object Uint16Array]',
'[object Int32Array]',
'[object Uint32Array]',
'[object Float32Array]',
'[object Float64Array]'
]
var isArrayBufferView = ArrayBuffer.isView || function (obj) {
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
}
}
function concatTypedarrays (chunks) {
var size = 0
var i = chunks.length
while (i--) { size += chunks[i].length }
var b = new Uint8Array(size)
var offset = 0
for (i = 0, l = chunks.length; i < l; i++) {
var chunk = chunks[i]
b.set(chunk, offset)
offset += chunk.byteLength || chunk.length
}
return b
}
/********************************************************/
/* Blob constructor */
/********************************************************/
function Blob (chunks, opts) {
chunks = chunks || []
opts = opts == null ? {} : opts
for (var i = 0, len = chunks.length; i < len; i++) {
var chunk = chunks[i]
if (chunk instanceof Blob) {
chunks[i] = chunk._buffer
} else if (typeof chunk === 'string') {
chunks[i] = textEncode(chunk)
} else if (arrayBufferSupported && (ArrayBuffer.prototype.isPrototypeOf(chunk) || isArrayBufferView(chunk))) {
chunks[i] = bufferClone(chunk)
} else if (arrayBufferSupported && isDataView(chunk)) {
chunks[i] = bufferClone(chunk.buffer)
} else {
chunks[i] = textEncode(String(chunk))
}
}
this._buffer = global.Uint8Array
? concatTypedarrays(chunks)
: [].concat.apply([], chunks)
this.size = this._buffer.length
this.type = opts.type || ''
if (/[^\u0020-\u007E]/.test(this.type)) {
this.type = ''
} else {
this.type = this.type.toLowerCase()
}
}
Blob.prototype.arrayBuffer = function () {
return Promise.resolve(this._buffer)
}
Blob.prototype.text = function () {
return Promise.resolve(textDecode(this._buffer))
}
Blob.prototype.slice = function (start, end, type) {
var slice = this._buffer.slice(start || 0, end || this._buffer.length)
return new Blob([slice], {type: type})
}
Blob.prototype.toString = function () {
return '[object Blob]'
}
/********************************************************/
/* File constructor */
/********************************************************/
function File (chunks, name, opts) {
opts = opts || {}
var a = Blob.call(this, chunks, opts) || this
a.name = name.replace(/\//g, ':')
a.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date()
a.lastModified = +a.lastModifiedDate
return a
}
File.prototype = create(Blob.prototype)
File.prototype.constructor = File
if (Object.setPrototypeOf) {
Object.setPrototypeOf(File, Blob)
} else {
try { File.__proto__ = Blob } catch (e) {}
}
File.prototype.toString = function () {
return '[object File]'
}
/********************************************************/
/* FileReader constructor */
/********************************************************/
function FileReader () {
if (!(this instanceof FileReader)) {
throw new TypeError("Failed to construct 'FileReader': Please use the 'new' operator, this DOM object constructor cannot be called as a function.")
}
var delegate = document.createDocumentFragment()
this.addEventListener = delegate.addEventListener
this.dispatchEvent = function (evt) {
var local = this['on' + evt.type]
if (typeof local === 'function') local(evt)
delegate.dispatchEvent(evt)
}
this.removeEventListener = delegate.removeEventListener
}
function _read (fr, blob, kind) {
if (!(blob instanceof Blob)) {
throw new TypeError("Failed to execute '" + kind + "' on 'FileReader': parameter 1 is not of type 'Blob'.")
}
fr.result = ''
setTimeout(function () {
this.readyState = FileReader.LOADING
fr.dispatchEvent(new Event('load'))
fr.dispatchEvent(new Event('loadend'))
})
}
FileReader.EMPTY = 0
FileReader.LOADING = 1
FileReader.DONE = 2
FileReader.prototype.error = null
FileReader.prototype.onabort = null
FileReader.prototype.onerror = null
FileReader.prototype.onload = null
FileReader.prototype.onloadend = null
FileReader.prototype.onloadstart = null
FileReader.prototype.onprogress = null
FileReader.prototype.readAsDataURL = function (blob) {
_read(this, blob, 'readAsDataURL')
this.result = 'data:' + blob.type + ';base64,' + array2base64(blob._buffer)
}
FileReader.prototype.readAsText = function (blob) {
_read(this, blob, 'readAsText')
this.result = textDecode(blob._buffer)
}
FileReader.prototype.readAsArrayBuffer = function (blob) {
_read(this, blob, 'readAsText')
// return ArrayBuffer when possible
this.result = (blob._buffer.buffer || blob._buffer).slice()
}
FileReader.prototype.abort = function () {}
/********************************************************/
/* URL */
/********************************************************/
URL.createObjectURL = function (blob) {
return blob instanceof Blob
? 'data:' + blob.type + ';base64,' + array2base64(blob._buffer)
: createObjectURL.call(URL, blob)
}
URL.revokeObjectURL = function (url) {
revokeObjectURL && revokeObjectURL.call(URL, url)
}
/********************************************************/
/* XHR */
/********************************************************/
var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
if (_send) {
XMLHttpRequest.prototype.send = function (data) {
if (data instanceof Blob) {
this.setRequestHeader('Content-Type', data.type)
_send.call(this, textDecode(data._buffer))
} else {
_send.call(this, data)
}
}
}
global.FileReader = FileReader
global.File = File
global.Blob = Blob
}
function fixFileAndXHR () {
var isIE = !!global.ActiveXObject || (
'-ms-scroll-limit' in document.documentElement.style &&
'-ms-ime-align' in document.documentElement.style
)
// Monkey patched
// IE don't set Content-Type header on XHR whose body is a typed Blob
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6047383
var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
if (isIE && _send) {
XMLHttpRequest.prototype.send = function (data) {
if (data instanceof Blob) {
this.setRequestHeader('Content-Type', data.type)
_send.call(this, data)
} else {
_send.call(this, data)
}
}
}
try {
new File([], '')
} catch (e) {
try {
var klass = new Function('class File extends Blob {' +
'constructor(chunks, name, opts) {' +
'opts = opts || {};' +
'super(chunks, opts || {});' +
'this.name = name.replace(/\//g, ":");' +
'this.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date();' +
'this.lastModified = +this.lastModifiedDate;' +
'}};' +
'return new File([], ""), File'
)()
global.File = klass
} catch (e) {
var klass = function (b, d, c) {
var blob = new Blob(b, c)
var t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date()
blob.name = d.replace(/\//g, ':')
blob.lastModifiedDate = t
blob.lastModified = +t
blob.toString = function () {
return '[object File]'
}
if (strTag) {
blob[strTag] = 'File'
}
return blob
}
global.File = klass
}
}
}
if (blobSupported) {
fixFileAndXHR()
global.Blob = blobSupportsArrayBufferView ? global.Blob : BlobConstructor
} else if (blobBuilderSupported) {
fixFileAndXHR()
global.Blob = BlobBuilderConstructor
} else {
FakeBlobBuilder()
}
if (strTag) {
File.prototype[strTag] = 'File'
Blob.prototype[strTag] = 'Blob'
FileReader.prototype[strTag] = 'FileReader'
}
var blob = global.Blob.prototype
var stream
function promisify(obj) {
return new Promise(function(resolve, reject) {
obj.onload =
obj.onerror = function(evt) {
obj.onload =
obj.onerror = null
evt.type === 'load'
? resolve(obj.result || obj)
: reject(new Error('Failed to read the blob/file'))
}
})
}
try {
new ReadableStream({ type: 'bytes' })
stream = function stream() {
var position = 0
var blob = this
return new ReadableStream({
type: 'bytes',
autoAllocateChunkSize: 524288,
pull: function (controller) {
var v = controller.byobRequest.view
var chunk = blob.slice(position, position + v.byteLength)
return chunk.arrayBuffer()
.then(function (buffer) {
var uint8array = new Uint8Array(buffer)
var bytesRead = uint8array.byteLength
position += bytesRead
v.set(uint8array)
controller.byobRequest.respond(bytesRead)
if(position >= blob.size)
controller.close()
})
}
})
}
} catch (e) {
try {
new ReadableStream({})
stream = function stream(blob){
var position = 0
var blob = this
return new ReadableStream({
pull: function (controller) {
var chunk = blob.slice(position, position + 524288)
return chunk.arrayBuffer().then(function (buffer) {
position += buffer.byteLength
var uint8array = new Uint8Array(buffer)
controller.enqueue(uint8array)
if (position == blob.size)
controller.close()
})
}
})
}
} catch (e) {
try {
new Response('').body.getReader().read()
stream = function stream() {
return (new Response(this)).body
}
} catch (e) {
stream = function stream() {
throw new Error('Include https://github.com/MattiasBuelens/web-streams-polyfill')
}
}
}
}
if (!blob.arrayBuffer) {
blob.arrayBuffer = function arrayBuffer() {
var fr = new FileReader()
fr.readAsArrayBuffer(this)
return promisify(fr)
}
}
if (!blob.text) {
blob.text = function text() {
var fr = new FileReader()
fr.readAsText(this)
return promisify(fr)
}
}
if (!blob.stream) {
blob.stream = stream
}
})()
2.2.3 FileReader调用方法
FileReader
可用来读取文本文件和图片文件,功能确实强大,希望前端开发者都可以多学习学习,精益求精。
//读取图片文件(如.jpg、.png、.gif等)
const fileReader = new FileReader();
fileReader.readAsDataURL(imgFile);
fileReader.onload = function() {
document.getElementById("img").src = fileReader.result;
console.log(img.getAttribute("src"));
}
//读取文本文件(如.txt、.json、.html、.js、.cs、.cpp、.py、.java、.m、.py、.dat、.csv、.cmd、.bat等)
const fileReader = new FileReader();
fileReader.readAsText(file);
fileReader.onload = function() {
document.getElementById("result").innerText = fileReader.result;
console.log(JSON.parse(fileReader.result));
};
3、input简介
在html
中,input
标签可用作输入框,为用户进行表单输入提供条件;另外,input
标签如果设置为file类型,则支持用户浏览本地文件。
accept 属性只能与 <input type="file"> 配合使用。它规定能够通过文件上传进行提交的文件类型。
值 描述
audio/* 接受所有的声音文件。
video/* 接受所有的视频文件。
image/* 接受所有的图像文件。
MIME_type 一个有效的 MIME 类型,不带参数。请参阅 IANA MIME 类型,获得标准 MIME 类型的完整列表。
accept可以指定如下信息:
*.3gpp audio/3gpp, video/3gpp 3GPP Audio/Video
*.ac3 audio/ac3 AC3 Audio
*.asf allpication/vnd.ms-asf Advanced Streaming Format
*.au audio/basic AU Audio
*.css text/css Cascading Style Sheets
*.csv text/csv Comma Separated Values
*.doc application/msword MS Word Document
*.dot application/msword MS Word Template
*.dtd application/xml-dtd Document Type Definition
*.dwg image/vnd.dwg AutoCAD Drawing Database
*.dxf image/vnd.dxf AutoCAD Drawing Interchange Format
*.gif image/gif Graphic Interchange Format
*.htm text/html HyperText Markup Language
*.html text/html HyperText Markup Language
*.jp2 image/jp2 JPEG-2000
*.jpe image/jpeg JPEG
*.jpeg image/jpeg JPEG
*.jpg image/jpeg JPEG
*.js text/javascript, application/javascript JavaScript
*.json application/json JavaScript Object Notation
*.mp2 audio/mpeg, video/mpeg MPEG Audio/Video Stream, Layer II
*.mp3 audio/mpeg MPEG Audio Stream, Layer III
*.mp4 audio/mp4, video/mp4 MPEG-4 Audio/Video
*.mpeg video/mpeg MPEG Video Stream, Layer II
*.mpg video/mpeg MPEG Video Stream, Layer II
*.mpp application/vnd.ms-project MS Project Project
*.ogg application/ogg, audio/ogg Ogg Vorbis
*.pdf application/pdf Portable Document Format
*.png image/png Portable Network Graphics
*.pot application/vnd.ms-powerpoint MS PowerPoint Template
*.pps application/vnd.ms-powerpoint MS PowerPoint Slideshow
*.ppt application/vnd.ms-powerpoint MS PowerPoint Presentation
*.rtf application/rtf, text/rtf Rich Text Format
*.svf image/vnd.svf Simple Vector Format
*.tif image/tiff Tagged Image Format File
*.tiff image/tiff Tagged Image Format File
*.txt text/plain Plain Text
*.wdb application/vnd.ms-works MS Works Database
*.wps application/vnd.ms-works Works Text Document
*.xhtml application/xhtml+xml Extensible HyperText Markup Language
*.xlc application/vnd.ms-excel MS Excel Chart
*.xlm application/vnd.ms-excel MS Excel Macro
*.xls application/vnd.ms-excel MS Excel Spreadsheet
*.xlt application/vnd.ms-excel MS Excel Template
*.xlw application/vnd.ms-excel MS Excel Workspace
*.xml text/xml, application/xml Extensible Markup Language
*.zip application/zip Compressed Archive
除上述类型外,2007后各文档如docx需配置的accept属性值如下:
Extension MIME Type
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xltx application/vnd.openxmlformats-officedocument.spreadsheetml.template
.potx application/vnd.openxmlformats-officedocument.presentationml.template
.ppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
.sldx application/vnd.openxmlformats-officedocument.presentationml.slide
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
.dotx application/vnd.openxmlformats-officedocument.wordprocessingml.template
.xlsm application/vnd.ms-excel.addin.macroEnabled.12
.xlsb application/vnd.ms-excel.sheet.binary.macroEnabled.12
3.1 input输入框用法(type为text)
input
标签设置为text
类型,用户可在框中进行输入文本。
<input style="background:transparent;border:2px solid rgb(231, 240, 240);color:rgb(74, 219, 230);height:30px;" id="website" value="http://mars3d.cn/project/vue/jcxm.html" />
<input id="json_filename" type="text" style="width:75%;background:transparent;" />
3.2 input 文件浏览按钮用法(type为file+button)
input
标签分别设置为file
和button
类型,用户可点击框选择本地文件。
<input type="file" id="up_jsonfile" name="jsonfile" style="display:none" required="required" accept="application/json, text/plain" onchange="readJSONData()" />
<input type="button" name="submit" style="width:15%;height:30px;border:1px solid #34495E;background-color:#5e3444;color:#ffffff" value="浏览json文件" onclick="select_jsonfile();" />
<input type="file" id="up_imagefile" name="imagefile" style="display:none" required="required" accept="image/gif, image/jpg, image/png" onchange="readImageData()" />
<input type="button" name="submit" style="width:15%;height:30px;border:1px solid #34495E;background-color:#9eca23;color:#ffffff" value="浏览图片文件" onclick="select_imagefile();" />
4、具体实现(以json文件和jpg文件为例)
假设本地计算机存在多个json
文件和多个jpg
文件,为了在浏览器上查看这些文件,只需将文件拖拽至浏览器中即可,但这种方式难免过于粗暴,那么下面具体以html
文档为例来进行实现文件的浏览查看及保存。
4.1 实现思路
具体的实现思路可分为如下三步
:
- 检测浏览器是否支持FileReader;
- 保存文本数据到json文件,保存canvas数据到图片文件;
- 浏览本地json文件进行查看并格式化,加载本地图片文件在img标签和canvas标签同时查看。
4.2 js代码(HTML文件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.js"></script>
<!-- https://github.com/eligrey/FileSaver.js -->
<style>
#myDiv {
background-image: url("https://img2.baidu.com/it/u=1721953618,4133119400&fm=253&fmt=auto&app=138&f=JPEG");
/* background: url("https://img2.baidu.com/it/u=1721953618,4133119400&fm=253&fmt=auto&app=138&f=JPEG"); */
width: 100%;
height: 100%;
z-index: -99;
}
.btn {
width: 100px;
height: 30px;
background: green;
border: none;
color: white;
margin: 6px 10px;
}
.btnStyle1 {
border-radius: 6px;
}
.btnStyle2 {
border-radius: 26px 6px;
}
.btnStyle3 {
border-radius: 6px 26px 60px;
}
.btnStyle4 {
border-radius: 6px 126px 236px 346px;
}
.bolder {
border: solid 1px green;
width: 500px;
height: 40px;
border-radius: 10px;
/* position: absolute;
left: 30%; */
position: relative;
margin-left: 35%;
}
.readFile {
width: 100%;
/* height:750px; */
background: transparent;
}
pre {
background-color: #01080fcc;
outline: 1px solid #ccc;
padding: 1px;
margin: 1px;
color: wheat;
}
.string {
color: greenyellow;
}
.number {
color: rgba(29, 191, 202, 0.925);
}
.boolean {
color: #cf7814;
}
.null {
color: rgba(108, 138, 163, 0.664);
}
.key {
color: rgb(225, 97, 129);
}
</style>
</head>
<body>
<div id="myDiv">
<div>
<button style="background:rgba(230, 230, 92, 0.5);width:100%;height:50px;color:white" onClick="alert('Question: Is your browser support FileReader ?'+'\n Answer:'+detect_FileReader())">检测浏览器是否支持FileReader</button>
</div>
<div style="background:transparent;width:100%;height:40px;font-size:large;margin-top:10px;font-size:30px;margin-bottom:30px;margin-left:800px;">
<input style="background:transparent;border:2px solid rgb(231, 240, 240);color:rgb(74, 219, 230);height:30px;" id="website" value="http://mars3d.cn/project/vue/jcxm.html" />
<a id="flyto" href="" target='_blank' onClick="document.getElementById('flyto').href = document.getElementById('website').value" style="color:rgb(226, 212, 19)">百度一下</a>
</div>
<div class="bolder">
<button class="btn btnStyle1" onClick="tip()">改变canvas</button>
<button class="btn btnStyle2" onClick="saveTxt()">保存txt文件</button>
<button class="btn btnStyle3" onClick="saveJSON()">保存json文件</button>
<button class="btn btnStyle4" onClick="savePicture()">保存图片文件</button>
</div>
<div class="readFile">
<div id="left" style="width:49%;float:left;border: blue 3px;background:transparent;border-right:3px solid salmon;">
<div style="width:100%;">
<!-- json文件浏览 -->
<div style="width:100%;color:coral">
<input id="json_filename" type="text" style="width:75%;background:transparent;" />
<input type="file" id="up_jsonfile" name="jsonfile" style="display:none" required="required" accept="application/json, text/plain" onchange="readJSONData()" />
<input type="button" name="submit" style="width:15%;height:30px;border:1px solid #34495E;background-color:#5e3444;color:#ffffff" value="浏览json文件" onclick="select_jsonfile();" />
</div>
<button type="button" style="width:100%;" onclick="ToJSON()" id="convertjson">将结果转为json格式</button>
<div style="width:100%;height:70%;margin-top:10px;color:coral">
<div id="leftjsonData" style="width:49%;float:left;">
<p id="result" style="color:#b0a117fa;width:100%;height:100%;overflow:auto;"></p>
</div>
<div id="rightjsonData" style="width:49%;float:left;">
<pre id="jsonresult" class="pre" style="float:left;width:100%;height:100%;overflow:auto;"></pre>
</div>
</div>
</div>
</div>
<div id="right" style="width:49%;float:left;border: red 3px;background:transparent;">
<div style="width:100%;">
<!-- 图片文件浏览 -->
<input id="image_filename" type="text" style="width:75%;background:transparent;" />
<input type="file" id="up_imagefile" name="imagefile" style="display:none" required="required" accept="image/gif, image/jpg, image/png" onchange="readImageData()" />
<input type="button" name="submit" style="width:15%;height:30px;border:1px solid #34495E;background-color:#9eca23;color:#ffffff" value="浏览图片文件" onclick="select_imagefile();" />
<div>
<table>
<tr>
<td>
<img src="" class="img-circle" style="width:300px;height:200px;" id="img">
</td>
<td>
<canvas width="640" height="480" id="tCanvas" style="border:black 1px solid;z-index:999"></canvas>
</td>
</tr>
<tr>
<td>
<strong><center>img标签中的图片</center></strong>
</td>
<td>
<strong><center>canvas标签中的图片</center></strong>
</td>
</tr>
</table>
</div>
</div>
<br>
</div>
</div>
</div>
<script>
let canvas = document.getElementById("tCanvas");
let context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.font = 'italic 40pt Calibri';
context.fillStyle = "red";
function detect_FileReader() {
if (window.FileReader) {
var fr = new FileReader();
return true;
} else {
return false;
}
}
function tip() {
//window.alert('jjg');
let width = 800,
height = 800;
canvas.width = width;
canvas.height = height;
context.fillRect(0, 0, width, height);
context.globalCompositeOperation = "difference";
// <!-- context.drawImage(image, 0, 0, width, height);-->
}
function saveTxt() {
var blob = new Blob(["Hello, world!"], {
type: "text/plain;charset=utf-8"
});
saveAs(blob, "hello world.txt");
}
function saveJSON() {
let tableDAta = [{
"date": "2016-05-02",
"name": "王小虎",
"address": "上海市普陀区金沙江路 1518 弄"
}, {
"date": "2016-05-04",
"name": "王小虎",
"address": "上海市普陀区金沙江路 1517 弄"
}, {
"date": "2016-05-01",
"name": "王小虎",
"address": "上海市普陀区金沙江路 1519 弄"
}, {
"date": "2016-05-03",
"name": "王小虎",
"address": "上海市普陀区金沙江路 1516 弄"
}];
let data = JSON.stringify(tableDAta, null, 3) //重点 第三参数代表缩进多少个空格
if (document.getElementById("result").innerText) {
data = document.getElementById("jsonresult").innerText;
alert(data);
}
let blob = new Blob([data], {
type: 'application/json;charset=utf-8'
})
saveAs(blob, `JJG_json.json`)
}
function savePicture() {
canvas.toBlob(function(blob) {
saveAs(blob, "pretty image.png");
});
}
function gettime() {
context.clearRect(0, 0, canvas.width, canvas.height);
var img = document.getElementById("img");
if (img.src != "") {
setInterval(function() {
context.drawImage(img, 0, 0, canvas.width, canvas.height); //context.drawImage(img, 0, 0, 300, 200, 0, 0, canvas.width, canvas.height);
var a = new Date();
var b = a.toLocaleTimeString();
var c = a.toLocaleDateString();
context.fillText(c + " " + b, 20, 50);
}, 20);
} else {
setInterval(function() {
var a = new Date();
var b = a.toLocaleTimeString();
var c = a.toLocaleDateString();
context.fillText(c + " " + b, 20, 50);
}, 20);
}
}
function select_jsonfile() {
document.getElementById('up_jsonfile').click();
}
function select_imagefile() {
document.getElementById('up_imagefile').click();
}
function readImageData() {
let _this = this;
const imageElement = document.getElementById("up_imagefile");
const imgFile = imageElement.files[0];
console.log(imgFile);
let fileinfotext = '文件名:' + imageElement.value + '\n文件类型:' + getFileType(imgFile) + '\n文件大小:' + imgFile.size + 'B';
document.getElementById('image_filename').value = fileinfotext;
const fileReader = new FileReader();
// console.log(imgFile);
fileReader.readAsDataURL(imgFile);
fileReader.onload = function() {
document.getElementById("img").src = fileReader.result;
console.log(img.getAttribute("src"));
_this.gettime();
}
}
function readJSONData() {
const jsonElement = document.getElementById('up_jsonfile');
const file = jsonElement.files[0];
if (getFileType(file) === 'json') {
let fileinfotext = '文件名:' + jsonElement.value + '\n文件类型:' + getFileType(file) + '\n文件大小:' + file.size + 'B';
document.getElementById('json_filename').value = fileinfotext;
}
if (window.FileReader) {
const fileReader = new FileReader();
fileReader.readAsText(file);
fileReader.onload = function() {
document.getElementById("result").innerText = fileReader.result;
// console.log(JSON.parse(fileReader.result));
document.getElementById("jsonresult").innerText = "";
};
}
}
function getFileType(file) {
if (file.type === 'image/png' || file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/gif') {
return 'image';
} else if (file.type === 'application/json') {
return 'json';
} else if (file.type === 'text/plain') {
return 'txt';
} else {
return 'other';
}
}
//对json数据进行高亮的函数
function syntaxHighlight(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 3);
}
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
//将请求结果转为json格式的函数
function ToJSON() {
if (document.getElementById("result").innerText) {
var Res = JSON.parse(document.getElementById("result").innerText, null, 3);
document.getElementById("jsonresult").innerHTML = syntaxHighlight(Res);
}
}
</script>
</body>
</html>
5、总结
简而言之,相关代码和方法皆参考Github,无论是工程师或是开发者,都喜欢用自己追捧的IDE
神器,目前除了Visual Studio和VS Code感觉还不错外,像PyCharm、Eclipse、MyEclipse、PyCharm、IDEA、WebStorm、Spyder、SublimeText
等尽管也还可以,但是对自己而言,个人觉得还是不要过于依赖这些IDE
,毕竟这些IDE
仅仅是利于编码的文件查看和编辑工具,开发人员万万不要被IDE
所束缚,具有想法和充满热情的工程师岂能为IDE折腰?愿大家都能够摆脱集成开发环境的束缚,从而得到轻松高效的编码乐趣,用技术改变世界,拥抱开源生态
。