我是靠谱客的博主 俭朴保温杯,最近开发中收集的这篇文章主要介绍JS在web中使用文件Using files from web applications,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

原文地址:developer.mozilla.org/zh-CN/docs/…

Using files from web applications

Jump to:

  1. 访问选择的文件
  2. Getting information about selected file(s)
  3. 获取被选择文件的信息
  4. Using hidden file input elements using the click() method
  5. 通过click()方法使用隐藏的file input元素
  6. Using a label element to trigger a hidden file input element
  7. 使用label元素来触发一个隐藏的input元素对应的事件
  8. Selecting files using drag and drop
  9. 使用drag和drap来选择文件
  10. Example: Showing thumbnails of user-selected images
  11. 例子:显示用户选择的图片的缩略图
  12. Using object URLs
  13. Example: Using object URLs to display images
  14. Example: Uploading a user-selected file
  15. Example: Using object URLs to display PDF
  16. Example: Using object URLs with other file types
  17. Specifications
  18. See also

Using the File API, which was added to the DOM in HTML5, it's now possible for web content to ask the user to select local files and then read the contents of those files. This selection can be done by either using an HTML

<input>

element or by drag and drop.

使用在HTML5中被加入DOM的File API,使页面内容请求用户选择本地文件,然后读取这些文件的内容成为可能。选择可以由使用 HTML

<input>元素或者通过drag 和 drop实现。

If you want to use the DOM File API from extensions or other browser chrome code, you can; however, note there are some additional features to be aware of. See

Using the DOM File API in chrome code

for details.

如果你想通过拓展或者其它的chrome源码浏览器(browser chrome code)使用DOM File API,是可行的;然而,注意有一些附加特性需要知道。看

Using the DOM File API in chrome code 了解详情。

访问选择的文件

考虑这段HTML:

<input type="file" id="input">
复制代码

The File API makes it possible to access a

FileList

containing

File

objects representing the files selected by the user.

File API使访问包含

File

对象的 FileList成为可能,FileList代表被用户选择的文件。

If the user selects just one file, it is then only necessary to consider the first file of the list.

如果用户只选择了一个文件,那么只需要考虑FileList中的第一个File对象。

Accessing one selected file using a classical DOM selector:

使用传统的DOM选择器访问一个被选择的文件。

var selectedFile = document.getElementById('input').files[0];
复制代码

Accessing selected file(s) on a change event

通过change事件访问被选择的文件

It is also possible (but not mandatory) to access the

FileList

through the

change

event:

可以通过change事件访问

FileList(但不是强制的)。

<input type="file" id="input" onchange="handleFiles(this.files)">
复制代码

When the user selects a file, the

handleFiles()

function gets called with a

FileListobject containing

File

objects representing the files selected by the user.

当用户选择一个文件时,handleFiles()方法会被调用,同时传入包含File对象的

FileList对象。File对象代表被用户选择的文件。

If you want to let the user select multiple files, simply use the

multiple

attribute on the

input

element:

如果你想让用户选择多个文件,只需在input元素上使用multiple属性:

<input type="file" id="input" multiple onchange="handleFiles(this.files)">
复制代码

In this case, the file list passed to the

handleFiles()

function contains one

File

object for each file the user selected.

在这个例子中,被传入handleFiles()方法的文件列表包含一组代表用户所选文件的 File

对象。

Dynamically adding a change listener

动态添加一个change监听

You need to use

EventTarget.addEventListener()

to add the

change

event listener, like this:

你需要使用EventTarget.addEventListener()去添加change事件监听,像这样:

var inputElement = document.getElementById("input");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
var fileList = this.files; /* now you can work with the file list */
}
复制代码

Note that in this case, the

handleFiles()

function itself is the event handler, unlike previous examples where it was called by an event handler which passed it a parameter.

注意在这个例子里,handleFiles()

方法本事是一个事件处理器,不像之前的例子中,它被事件处理器调用然后传递给它一个参数。

Getting information about selected file(s)

获取被选择文件的信息

The

FileList

object provided by the DOM lists all of the files selected by the user, each specified as a

File

object. You can determine how many files the user selected by checking the value of the file list's

length

attribute:

FileList

对象由DOM提供,列出了所有用户选择的文件,每一个代表了一个

File

对象。你可以通过检查文件列表的length属性决定用户可以选则多少文件

var numFiles = files.length;
复制代码

Individual

File

objects can be retrieved by simply accessing the list as an array:

非法的File对象可以通过访问文件列表被恢复,像访问数组那样。

for (var i = 0, numFiles = files.length; i < numFiles; i++) {
var file = files[i];
..
}
复制代码

This loop iterates over all the files in the file list.

这个循环迭代文件列表里的所有文件。

There are three attributes provided by the

File

object that contain useful information about the file.

这是

File

对象提供的三个属性,包含了关于文件的一些有价值的信息。

`name`
The file's name as a read-only string. This is just the file name, and does not include any path information.
文件名称,只读字符串。只包含文件名称,不包含任何路径信息。
`size`
The size of the file in bytes as a read-only 64-bit integer.
文件大小,使用bytes描述,是一个只读的64位整数.
`type`
The MIME type of the file as a read-only string or

""

if the type couldn't be determined.

文件的MIME type,只读字符串,当类型不能确定时为`""。`

Example: Showing file(s) size

例子:显示文件大小

The following example shows a possible use of the

size

property:

下面的例子展示了size属性的一种可能用法。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>File(s) size</title>
<script>
function updateSize() {
var nBytes = 0,
oFiles = document.getElementById("uploadInput").files,
nFiles = oFiles.length;
for (var nFileId = 0; nFileId < nFiles; nFileId++) {
nBytes += oFiles[nFileId].size;
}
var sOutput = nBytes + " bytes";
// optional code for multiples approximation
for (var aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
}
// end of optional code
document.getElementById("fileNum").innerHTML = nFiles;
document.getElementById("fileSize").innerHTML = sOutput;
}
</script>
</head>
<body onload="updateSize();">
<form name="uploadForm">
<p><input id="uploadInput" type="file" name="myFiles" onchange="updateSize();" multiple> selected files: <span id="fileNum">0</span>; total size: <span id="fileSize">0</span></p>
<p><input type="submit" value="Send file"></p>
</form>
</body>
</html>
复制代码

Using hidden file input elements using the click() method

通过click()方法使用隐藏的file input元素

Starting in Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1), you can hide the admittedly ugly file

<input>

element and present your own interface for opening the file picker and displaying which file or files the user has selected. You can do this by styling the input element with

display:none

and calling the

click()

method on the

<input>element.

从Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)开始,你可以隐藏丑陋的 file

<input>

元素然后使用你自己的接口打开文件选择器,然后显示哪个文件(或多个)被用户选择了。你可以通过给input元素添加display:none的样式然后在

<input>

元素上调用click()方法。

Consider this HTML:

考虑这段HTML:

<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<a href="#" id="fileSelect">Select some files</a>
复制代码

The code that handles the

click

event can look like this:

处理点击事件的代码可以像这样:

var fileSelect = document.getElementById("fileSelect"),
fileElem = document.getElementById("fileElem");
fileSelect.addEventListener("click", function (e) {
if (fileElem) {
fileElem.click();
}
e.preventDefault(); // prevent navigation to "#"
}, false);
复制代码

You can style the new button for opening the file picker as you wish.

你可以给这个用来打开文件选择器新按钮添加任何你想要的样式。

Using a label element to trigger a hidden file input element

使用label元素来触发一个隐藏的input元素对应的事件

To allow opening the file picker without using JavaScript (the click() method), a

<label>element can be used.

如果想不使JavaScript (click() 方法)打开文件选择器,使用

<label>

元素也可以。

Consider this HTML:

考虑这段HTML

<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<label for="fileElem">Select some files</label>
复制代码

There is no need to add JavaScript code to call

fileElem.click(). Also in this case you can style the label element as you wish.

没有必要添加JavaScript代码调用fileElem.click().在这个例子里你也可以给label元素添加你想要的样式

Selecting files using drag and drop

使用drag和drap来选择文件

You can also let the user drag and drop files into your web application.

你可以在让用户使用拖拽将文件传入你的web应用中。

The first step is to establish a drop zone. Exactly what part of your content will accept drops may vary depending on the design of your application, but making an element receive drop events is easy:

第一步是创建一个drop区域。就是那在个你内容中将要接收拖放的部分,这部分根据你的设计而定,但是使一个元素接收drop事件是很容易的。

var dropbox;
dropbox = document.getElementById("dropbox");
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);
复制代码

In this example, we're turning the element with the ID

dropbox

into our drop zone. This is done by adding listeners for the

[dragenter](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/dragenter "/zh-CN/docs/Web/Reference/Events/dragenter"),

[dragover](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/dragover "/zh-CN/docs/Web/Reference/Events/dragover"), and

[drop](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/drop "/zh-CN/docs/Web/Reference/Events/drop")

events.

在这个例子中,我将ID为dropbox的元素转换成我们的drop区域。这个操作通过给元素添加[dragenter](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/dragenter "/zh-CN/docs/Web/Reference/Events/dragenter"),

[dragover](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/dragover "/zh-CN/docs/Web/Reference/Events/dragover"), 和[drop](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/drop "/zh-CN/docs/Web/Reference/Events/drop")

事件监听完成。

We don't actually need to do anything with the

dragenter

and

dragover

events in our case, so these functions are both simple. They just stop propagation of the event and prevent the default action from occurring:

我们其实并不需要对dragenter

and

dragover

事件进行处理,所以这些函数都可以很简单。他们只需要包括禁止事件传播和阻止默认事件。

function dragenter(e) {
e.stopPropagation();
e.preventDefault();
}
function dragover(e) {
e.stopPropagation();
e.preventDefault();
}
复制代码

The real magic happens in the

drop()

function:

真正的戏法发生在drop这个函数中:

function drop(e) {
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
handleFiles(files);
}
复制代码

Here, we retrieve the

dataTransfer

field from the event, pull the file list out of it, and then pass that to

handleFiles(). From this point on, handling the files is the same whether the user used the

input

element or drag and drop.

这里,我们从事件中获取到了dataTransfer

这个域,然后从中得到文件列表,再将他们传递给handleFiles函数。从这一点开始,处理文件的方法和用input或者用拖拽是一样的了。

Example: Showing thumbnails of user-selected images

例子:显示用户选择的图片的缩略图

Let's say you're developing the next great photo-sharing website and want to use HTML5 to display thumbnail previews of images before the user actually uploads them. You can establish your input element or drop zone as discussed previously and have them call a function such as the

handleFiles()

function below.

比方说,你正在开发一个很棒的下一代图片分享网站,并且想使用HTML5来展示用户在实际上传之前的图片的缩略图。你可以创建自己的input元素或者drap区域就像我们之前讨论的那样,然后对他们使用一个回调函数比如下面的handleFiles()

function handleFiles(files) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /^image//;
if (!imageType.test(file.type)) {
continue;
}
var img = document.createElement("img");
img.classList.add("obj");
img.file = file;
preview.appendChild(img); // Assuming that "preview" is the div output where the content will be displayed.
var reader = new FileReader();
reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
reader.readAsDataURL(file);
}
}
复制代码

Here our loop handling the user-selected files looks at each file's

type

attribute to see if it's an image file (by doing a regular expression match on the MIME type string pattern "image/*"). For each file that is an image, we create a new

img

element. CSS can be used to establish any pretty borders or shadows and to specify the size of the image, so that doesn't need to be done here.

这里我们循环处理用户选择的文件,看每个文件的type属性是不是image(通过正则表达式来匹配MIME类型字符串模式"image/*")。 对每个文件而言,如果它是图片,我们就创建一个img元素。可以使用css来创建一个漂亮的边框或阴影来显示图片的具体大小,这儿就不具体做了。

Each image has the CSS class

obj

added to it, making it easy to find in the DOM tree. We also add a

file

attribute to each image specifying the

File

for the image; this will let us fetch the images for actual upload later. We use

Node.appendChild()

to add the new thumbnail to the preview area of our document.

为了在DOM树中更容易地找到他们,每个图片元素都被添加了一个名为obj的class。我们还给每个图片添加了file属性使它具有

File;这样做可以让我们拿到稍后需要实际上传的图片。我们在预览页中使用

Node.appendChild()来添加新的缩略图。

Next, we establish the

FileReader

to handle asynchronously loading the image and attaching it to the

img

element. After creating the new

FileReader

object, we set up its

onload

function and then call

readAsDataURL()

to start the read operation in the background. When the entire contents of the image file are loaded, they are converted into a

data:

URL which is passed to the

onload

callback. Our implementation of this routine sets the

img

element's

src

attribute to the loaded image which results in the image appearing in the thumbnail on the user's screen.

接下来,我们创建了FileReader来处理异步的图片加载并把他赋给图片元素。在创建一个新的

FileReader对象后,我们新建了它的onload

函数,然后调用readAsDataURL函数在后台开始读取文件的操作。当整个图片文件被全部加载完后,他们被转换成了一个被传递到onload回调函数的data:URL。我们再执行常规操作将img元素的src属性设置为刚刚加载完毕的URL,使得图像可以显示在用户屏幕上的缩略图中。

Using object URLs

Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) introduces support for the DOM

window.URL.createObjectURL()

and

window.URL.revokeObjectURL()

methods. These let you create simple URL strings that can be used to reference any data that can be referred to using a DOM

File

object, including local files on the user's computer.

When you have a

File

object you'd like to reference by URL from HTML, you can create an object URL for it like this:

var objectURL = window.URL.createObjectURL(fileObj);
复制代码

The object URL is a string identifying the

File

object. Each time you call

window.URL.createObjectURL(), a unique object URL is created even if you've created an object URL for that file already. Each of these must be released. While they are released automatically when the document is unloaded, if your page uses them dynamically you should release them explicitly by calling

window.URL.revokeObjectURL():

window.URL.revokeObjectURL(objectURL);
复制代码

Example: Using object URLs to display images

This example uses object URLs to display image thumbnails. In addition, it displays other file information including their names and sizes.

The HTML that presents the interface looks like this:

<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<a href="#" id="fileSelect">Select some files</a>
<div id="fileList">
<p>No files selected!</p>
</div>
复制代码

This establishes our file

<input>

element as well as a link that invokes the file picker (since we keep the file input hidden to prevent that less-than-attractive user interface from being displayed). This is explained in the section

Using hidden file input elements using the click() method, as is the method that invokes the file picker.

The

handleFiles()

method follows:

window.URL = window.URL || window.webkitURL;
var fileSelect = document.getElementById("fileSelect"),
fileElem = document.getElementById("fileElem"),
fileList = document.getElementById("fileList");
fileSelect.addEventListener("click", function (e) {
if (fileElem) {
fileElem.click();
}
e.preventDefault(); // prevent navigation to "#"
}, false);
function handleFiles(files) {
if (!files.length) {
fileList.innerHTML = "<p>No files selected!</p>";
} else {
fileList.innerHTML = "";
var list = document.createElement("ul");
fileList.appendChild(list);
for (var i = 0; i < files.length; i++) {
var li = document.createElement("li");
list.appendChild(li);
var img = document.createElement("img");
img.src = window.URL.createObjectURL(files[i]);
img.height = 60;
img.onload = function() {
window.URL.revokeObjectURL(this.src);
}
li.appendChild(img);
var info = document.createElement("span");
info.innerHTML = files[i].name + ": " + files[i].size + " bytes";
li.appendChild(info);
}
}
}
复制代码

This starts by fetching the URL of the

<div>

with the ID

fileList. This is the block into which we'll insert our file list, including thumbnails.

If the

FileList

object passed to

handleFiles()

is

null, we simply set the inner HTML of the block to display "No files selected!". Otherwise, we start building our file list, as follows:

  1. A new unordered list (<ul>) element is created.

  2. The new list element is inserted into the

    <div>

    block by calling its

    Node.appendChild()

    method.

  3. For each

    File

    in the

    FileList

    represented by

    files:

    1. Create a new list item (<li>) element and insert it into the list.

    2. Create a new image (<img>) element.

    3. Set the image's source to a new object URL representing the file, using

      window.URL.createObjectURL()

      to create the blob URL.

    4. Set the image's height to 60 pixels.

    5. Set up the image's load event handler to release the object URL since it's no longer needed once the image has been loaded. This is done by calling the

      window.URL.revokeObjectURL()

      method and passing in the object URL string as specified by

      img.src.

    6. Append the new list item to the list.

Here is a live demo of the code above:

在 CodePen 中打开 在 JSFiddle 中打开

Example: Uploading a user-selected file

Another thing you might want to do is let the user upload the selected file or files (such as the images selected using the previous example) to a server. This can be done asynchronously very easily.

Creating the upload tasks

Continuing with the code that built the thumbnails in the previous example, recall that every thumbnail image is in the CSS class

obj

with the corresponding

File

attached in a

file

attribute. This allows us to select all of the images the user has chosen for uploading using

Document.querySelectorAll(), like this:

function sendFiles() {
var imgs = document.querySelectorAll(".obj");
for (var i = 0; i < imgs.length; i++) {
new FileUpload(imgs[i], imgs[i].file);
}
}
复制代码

Line 2 fetches a

NodeList

, called

imgs

, of all the elements in the document with the CSS class

obj

. In our case, these will be all of the image thumbnails. Once we have that list, it's trivial to go through it and create a new

FileUpload

instance for each. Each of these handles uploading the corresponding file.

Handling the upload process for a file

The

FileUpload

function accepts two inputs: an image element and a file from which to read the image data.

function FileUpload(img, file) {
var reader = new FileReader();
this.ctrl = createThrobber(img);
var xhr = new XMLHttpRequest();
this.xhr = xhr;
var self = this;
this.xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
var percentage = Math.round((e.loaded * 100) / e.total);
self.ctrl.update(percentage);
}
}, false);
xhr.upload.addEventListener("load", function(e){
self.ctrl.update(100);
var canvas = self.ctrl.ctx.canvas;
canvas.parentNode.removeChild(canvas);
}, false);
xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
reader.onload = function(evt) {
xhr.send(evt.target.result);
};
reader.readAsBinaryString(file);
}
复制代码

The

FileUpload()

function shown above creates a throbber, which is used to display progress information, and then creates an

XMLHttpRequest

to handle uploading the data.

Before actually transferring the data, several preparatory steps are taken:

  1. The

    XMLHttpRequest's upload

    progress

    listener is set to update the throbber with new percentage information so that as the upload progresses the throbber will be updated based on the latest information.

  2. The

    XMLHttpRequest's upload

    load

    event handler is set to update the throbber progress information to 100% to ensure the progress indicator actually reaches 100% (in case of granularity quirks during the process). It then removes the throbber since it's no longer needed. This causes the throbber to disappear once the upload is complete.

  3. The request to upload the image file is opened by calling

    XMLHttpRequest's

    open()method to start generating a POST request.

  4. The MIME type for the upload is set by calling the

    XMLHttpRequest

    function

    overrideMimeType(). In this case, we're using a generic MIME type; you may or may not need to set the MIME type at all depending on your use case.

  5. The

    FileReader

    object is used to convert the file to a binary string.

  6. Finally, when the content is loaded the

    XMLHttpRequest

    function

    send()

    is called to upload the file's content.

Note:

The non-standard

sendAsBinary

method which was previously used in the example above is considered deprecated as of Gecko 31 (Firefox 31 / Thunderbird 31 / SeaMonkey 2.28); use the standard

send(Blob data)

method instead.

Handling the upload process for a file, asynchronously

<?php
if (isset($_FILES['myFile'])) {
// Example:
move_uploaded_file($_FILES['myFile']['tmp_name'], "uploads/" . $_FILES['myFile']['name']);
exit;
}
?><!DOCTYPE html>
<html>
<head>
<title>dnd binary upload</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
function sendFile(file) {
var uri = "/index.php";
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.open("POST", uri, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText); // handle response.
}
};
fd.append('myFile', file);
// Initiate a multipart/form-data upload
xhr.send(fd);
}
window.onload = function() {
var dropzone = document.getElementById("dropzone");
dropzone.ondragover = dropzone.ondragenter = function(event) {
event.stopPropagation();
event.preventDefault();
}
dropzone.ondrop = function(event) {
event.stopPropagation();
event.preventDefault();
var filesArray = event.dataTransfer.files;
for (var i=0; i<filesArray.length; i++) {
sendFile(filesArray[i]);
}
}
}
</script>
</head>
<body>
<div>
<div id="dropzone" style="margin:30px; width:500px; height:300px; border:1px dotted grey;">Drag & drop your file here...</div>
</div>
</body>
</html>
复制代码

Example: Using object URLs to display PDF

Object URLs can be used for other things than just images! They can be used to display embedded PDF files or any other resources that can be displayed by the browser.

In Firefox, to have the PDF appear embedded in the iframe (rather than proposed as a downloaded file), the preference

pdfjs.disabled

must be set to

false

.

<iframe id="viewer">
复制代码

And here is the change of the

src

attribute:

var obj_url = window.URL.createObjectURL(blob);
var iframe = document.getElementById('viewer');
iframe.setAttribute('src', obj_url);
window.URL.revokeObjectURL(obj_url);
复制代码

Example: Using object URLs with other file types

You can manipulate files of other formats the same way. Here is how to preview uploaded video:

var video = document.getElementById('video');
var obj_url = window.URL.createObjectURL(blob);
video.src = obj_url;
video.play()
window.URL.revokeObjectURL(obj_url);
复制代码

Specifications

  • File upload state

    (HTML 5 working draft)

  • File API

See also

  • File
  • FileList
  • FileReader
  • Using XMLHttpRequest
  • Using the DOM File API in chrome code
  • XMLHttpRequest

HTML5 Documentation

| HTML |

  • Audio/Video
  • Canvas
  • WebGL
  • SVG
  • MathML
  • WebForms
  • AppCache
  • Microformats
  • SemanticTags

| | JavaScript |

  • Storage
  • IndexedDB
  • WebSockets
  • WebWorkers
  • Events
  • Drag/Drop
  • ProtocolHandler
  • Geolocation
  • Focus

| | CSS |

  • NewSelectors
  • Typography
  • Visual

|

转载于:https://juejin.im/post/5c37115e5188252410606b9c

最后

以上就是俭朴保温杯为你收集整理的JS在web中使用文件Using files from web applications的全部内容,希望文章能够帮你解决JS在web中使用文件Using files from web applications所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(60)

评论列表共有 0 条评论

立即
投稿
返回
顶部