HTML文件上傳與下載

文件下載

傳統的文件下載有兩種方法:

  1. 使用<a/>標簽,href屬性直接連接到服務器的文件路徑
  2. window.location.href="url"

這兩種方法效果一樣。但有個很大的問題,如果下載出現異常(連接路徑失效、文件不存在、網絡問題等),會導致原本的頁面被覆蓋掉,顯示404等錯誤信息。

大致的優化思路如下:

  1. 使用<a/>標簽HTML5新的屬性download。
  2. 使用<iframe><iframe/>元素進行下載。
  3. 使用ajax、axios、fetch等方法異步下載。
  4. 使用websocket下載。

我們來逐一分析:

  1. ?<a/>標簽的download屬性,需要和href一起用,download的作用是為下載的文件賦文件名。
    • 如果服務端沒有指定文件名,就以此屬性規定的名稱命名。
    • 如果下載出現異常,該屬性的存在能夠保證頁面不會出問題。
    • 如果服務端返回的不是文件、而是字符,如果download=‘’error.txt”,能夠通過打開此文件查看到返回的文本信息。
  2. <iframe>標簽可以做到在現有的頁面下,內嵌一個子頁面。當用戶點擊文件下載時,將隱藏的iframe元素的src屬性指向文件下載路徑。
    • 如果沒有異常,文件將會直接下載。
    • 如果出現異常,iframe子頁面會報錯,父頁面不會受任何影響。
  3. 使用異步請求進行下載。
    • 在網上看了看,大致的流程是:發送異步請求時設置responseType為blob,即接收流數據為blob對象保存在內存中。接收完成后,生成鏈接地址(1.通過FileReader對象將blob對象生成base64編碼 2.通過URL.createObjectURL生成指向文件內存的鏈接),寫入<a/>標簽的href屬性,然后模擬點擊<a/>按標簽實現下載。
    • 此方法最大的問題是,因無法直接操作磁盤,故接收的文件必須先存放在內存中(且只有傳輸完成后才能構建blob對象),才能轉化成文件。因此,大文件的下載可能會把你的瀏覽器擠爆。
  4. 使用websocket下載。
    • 需要額外開啟websocket服務,此方法未做實踐。

總結以上方法,最推薦前兩種,方便簡單。

附上后端Django代碼(適用于前兩種方法):

def syncDownLoad(request):"文件下載"print("同步下載文件")startTime = time.time()def file_iterator(file, chunk_size=1024):with open(file, "rb") as f:while True:c = f.read(chunk_size)if c:yield celse:endTime = time.time()print("傳輸時間", endTime - startTime)breakfileRoute = "/static/files/2018/12/18/第四章(1)學習動機概述.mp4"fileName = "第四章(1)學習動機概述.mp4"route = os.path.dirname(os.path.dirname(__file__)) + fileRouteif os.path.exists(route):  # 如果存在文件response = StreamingHttpResponse(file_iterator(route))# response['Content-Type'] = 'application/octet-stream'response['Content-Type'] = 'text/html'response['Content-Disposition'] = 'attachment;filename="{0}"'.format(fileName).encode("utf-8")return responseelse:return HttpResponse("cannot find file")

參考鏈接:

https://scarletsky.github.io/2016/07/03/download-file-using-javascript/

https://my.oschina.net/watcher/blog/1525962

文件上傳

?概述

文件上傳需要處理的問題有:

1.多文件上傳? 2.異步上傳? 3.拖拽上傳? 4.上傳限制(限制大小、類型) 5.顯示上傳進度、上傳速度、中途取消上傳? 6.預覽文件

HTML DEMO

<input type="file" id="file" name="myfile" οnchange="onchanges()" multiple="multiple"/>
<input type="button" οnclick="SerialUploadFile()" value="上傳"/>

一、多文件上傳

<input type="file" id="file" name="myfile" multiple="multiple"/> <!-- multiple屬性 -->

二、異步上傳

通過ajax等方式異步上傳,FormData對象支持傳輸文件。

function UploadFile() {var fileObj = document.getElementById("file").files;  // js 獲取文件對象(FileList對象)// FormData 對象var form = new FormData();form.append("author", "xueba");             // 可以增加表單數據for (let i = 0; i < fileObj.length; i++){form.append("file", fileObj[i]);        // 文件對象}$.ajax({url: "/file_upload/",type: "POST",async: true,      // 異步上傳data: form,contentType: false, // 必須false才會自動加上正確的Content-TypeprocessData: false, // 必須false才會避開jQuery對 formdata 的默認處理。XMLHttpRequest會對 formdata 進行正確的處理success: function (data) {data = JSON.parse(data);data.forEach((i)=>{console.log(i.code,i.file_url);});},error: function () {alert("aaa上傳失敗!");},});}

三、拖拽上傳

默認文本、圖像和鏈接可以被拖動。其它的元素想要被拖動,只需為標簽加一個draggable="true"屬性

<div draggable="true"><div/>

?HTML5 API drag 和 drop

被拖動元素發生的事件dragstart    被拖動元素開始拖動時drag         正在被拖動時dragend      取消拖拽時目標元素發生的事件(當某元素被綁定以下事件就變成了目標元素)dragenter    拖動元素進入目標上觸發dragover     拖動元素在目標元素上移動觸發dragleave    拖動元素離開目標時觸發drop         拖動元素在目標上釋放觸發,這時不會觸發dragleave注意:1.目標元素默認不能夠被拖放drop,要在dragover事件中取消默認事件(e.preventDefault())2.有些元素(img)被拖放后,默認以鏈接形式打開,要在drop事件中取消默認事件(e.preventDefault())【火狐瀏覽器可能不頂用,需要再加event.stopPropagation()】dataTransfer(事件對象屬性(對象))數據交換:只是簡單的拖拽沒有意義,我們還需要數據交換,即被拖動元素和目標元素之間的數據交換。方法:setData(key,value)  設置數據(key和value都必須是string類型)getData(key)        獲取數據clearData()         清除數據(不傳參清空所有數據)setDragImage(imgElement,x,y)      設置元素移動過程中的圖像(參數:圖像元素,xy表示圖像內的偏移量)屬性:dropEffect  表示被拖動元素可以執行哪一種放置行為(一般在dragover事件內設置)none禁止放置(默認值)   move移動到新的位置   copy復制到新的位置 linkeffectAllowed  用來指定拖動時被允許的行為(一般無需設置)copy,move,link,copyLink,copyMove,linkMove,all,none,uninitialized默認值,相當于all.files    FileList對象。如果拖動的不是文件,此為空列表     items    返回DataTransferItems對象,該對象代表了拖動數據。types    返回一個DOMStringList對象,該對象包括了存入dataTransfer中數據的所有類型。注意:1.如果拖拽了文本,瀏覽器會自動調用setData(),設置對應文本數據

該功能沒有Demo

參考鏈接:

https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API

https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer

https://www.zhangxinxu.com/wordpress/2018/09/drag-drop-datatransfer-js/

http://www.sohu.com/a/198973397_291052

四、上傳限制

<input type="file"  accept="image/*" /> 接收全部格式的圖片

此外,獲取到的File對象中有type屬性可以得知文件類型,size屬性的得知文件大小

五、上傳進度、上傳速度、中途取消上傳

原生API

xhr.onload = function(e){};//上傳請求完成
xhr.onerror = function(e){};//上傳異常
xhr.upload.onloadstart = function(e){};//開始上傳
xhr.upload.onprogress =function(e){};//上傳進度  這個方法會在文件每上傳一定字節時調用e.loaded//表示已經上傳了多少byte的文件大小
e.total//表示文件總大小為多少byte
通過這兩個關鍵的屬性就可以去計算 上傳進度與速度xhr.onreadystatechange = function(){}//當xhr的狀態(上傳開始,結束,失敗)變化時會調用 該方法可以用來接收服務器返回的數據中途取消上傳 xhr.abort();

單文件上傳 或 多文件串行上傳 Demo:(該Demo只會有一個進度條,顯示上傳總進度。對應“異步上傳”的代碼)

xhr.upload.addEventListener("progess",progessSFunction,false); // 上傳過程中顯示進度和速度function progressSFunction(e) {var progressBar = document.getElementById(`pro`);var percentageDiv = document.getElementById(`per`);if (e.lengthComputable) // lengthComputable表示進度信息是否可用{progressBar.max = e.total;progressBar.value = e.loaded;let speed = (e.loaded - progress[0].last_laoded) / (e.timeStamp - progress[0].last_time) + " bytes/s";let percent = Math.round(e.loaded / e.total * 100) + "%";progress[0].last_laoded = e.loaded, progress[0].last_time = e.timeStamp;percentageDiv.innerHTML = percent + " " + speed;}}

多文件并行上傳進度顯示:(多個進度條,分別上傳)

// 多文件并行上傳function ParallelUploadFile() {last_laoded = 0;last_time = (new Date()).getTime();var fileObj = document.getElementById("file").files;  // js 獲取文件對象for (let k = 0; k < fileObj.length; k++){let domStr = `<div> ${fileObj[k].name},大小${fileObj[k].size}字節<progress class='progressBar' id='pro${k}' value='' max=''></progress><span class='percentage' id='per${k}'></span></div>`;$("body").append(domStr);// FormData 對象var form = new FormData();form.append("author", "xueba");             // 可以增加表單數據form.append("csrfmiddlewaretoken", $("[name = 'csrfmiddlewaretoken']").val());form.append("file", fileObj[k]);// XMLHttpRequest 對象{#var xhr = new XMLHttpRequest();#}{#xhr.open("post", "/file_upload/", true);#}{#xhr.onload = function () {#}{#   alert("上傳完成!");#}{# };#}{#xhr.upload.addEventListener("progress", progressFunction, false);#}{#xhr.send(form);#}// jQuery ajax$.ajax({url: "/file_upload/",type: "POST",async: true,      // 異步上傳data: form,contentType: false, // 必須false才會自動加上正確的Content-TypeprocessData: false, // 必須false才會避開jQuery對 formdata 的默認處理。XMLHttpRequest會對 formdata 進行正確的處理xhr: function () {let xhr = $.ajaxSettings.xhr();xhr.upload.addEventListener("progress", (e) => {progressPFunction(e, k)}, false);xhr.upload.onloadstart = (e) => {progress[k] = {last_laoded: 0,last_time: e.timeStamp,};};xhr.upload.onloadend = () => {delete progress[k];};return xhr;},success: function (data) {data = JSON.parse(data);data.forEach((i) => {console.log(i.code, i.file_url);});},error: function () {alert("aaa上傳失敗!");},});}}

六、預覽文件

預覽圖片

function onchanges() { // input file綁定onchange事件let files = document.getElementById("file").files;if(files[0].type.indexOf("image")>-1){let read = new FileReader();read.onload = function(e) { // 讀取操作完成時觸發let img = new Image();img.src = e.target.result; // 將base64編碼賦給src屬性$("body")[0].appendChild(img);};read.readAsDataURL(files[0]); // 讀取文件轉化成base64編碼}
}?

七、前后端匯總Demo

前端

HTML

<input type="file" id="file" name="myfile" οnchange="onchanges()" multiple="multiple"/>
<input type="button" οnclick="SerialUploadFile()" value="上傳"/>

JavaScript

   let progress = {};let last_laoded;let last_time;function onchanges() {let files = document.getElementById("file").files;console.log(`共${files.length}個文件`);let countSize = 0;for (let i = 0; i < files.length; i++) {console.log(`${files[i].name}  大小${files[i].size}`);countSize += files[i].size;}console.log(`共計占用${countSize}字節`);if (files[0].type.indexOf("image") > -1){let read = new FileReader();read.onload = function (e) { // 讀取操作完成時觸發let img = new Image();img.src = e.target.result; // 將base64編碼賦給src屬性$("body")[0].appendChild(img);};read.readAsDataURL(files[0]); // 讀取文件轉化成base64編碼}}// 多文件并行上傳function ParallelUploadFile() {last_laoded = 0;last_time = (new Date()).getTime();var fileObj = document.getElementById("file").files;  // js 獲取文件對象for (let k = 0; k < fileObj.length; k++){let domStr = `<div> ${fileObj[k].name},大小${fileObj[k].size}字節<progress class='progressBar' id='pro${k}' value='' max=''></progress><span class='percentage' id='per${k}'></span></div>`;$("body").append(domStr);// FormData 對象var form = new FormData();form.append("author", "xueba");             // 可以增加表單數據form.append("csrfmiddlewaretoken", $("[name = 'csrfmiddlewaretoken']").val());form.append("file", fileObj[k]);// XMLHttpRequest 對象{#var xhr = new XMLHttpRequest();#}{#xhr.open("post", "/file_upload/", true);#}{#xhr.onload = function () {#}{#   alert("上傳完成!");#}{# };#}{#xhr.upload.addEventListener("progress", progressFunction, false);#}{#xhr.send(form);#}// jQuery ajax$.ajax({url: "/file_upload/",type: "POST",async: true,      // 異步上傳data: form,contentType: false, // 必須false才會自動加上正確的Content-TypeprocessData: false, // 必須false才會避開jQuery對 formdata 的默認處理。XMLHttpRequest會對 formdata 進行正確的處理xhr: function () {let xhr = $.ajaxSettings.xhr();xhr.upload.addEventListener("progress", (e) => {progressPFunction(e, k)}, false);xhr.upload.onloadstart = (e) => {progress[k] = {last_laoded: 0,last_time: e.timeStamp,};};xhr.upload.onloadend = () => {delete progress[k];};return xhr;},success: function (data) {data = JSON.parse(data);data.forEach((i) => {console.log(i.code, i.file_url);});},error: function () {alert("aaa上傳失敗!");},});}}// 多文件串行上傳function SerialUploadFile() {var fileObj = document.getElementById("file").files;  // js 獲取文件對象let domStr = `<div><progress class='progressBar' id='pro' value='' max=''></progress><span class='percentage' id='per'></span></div>`;$("body").append(domStr);// FormData 對象var form = new FormData();form.append("author", "xueba");             // 可以增加表單數據for (let i = 0; i < fileObj.length; i++){form.append("file", fileObj[i]);        // 文件對象}// jQuery ajax$.ajax({url: "/file_upload/",type: "POST",async: true,      // 異步上傳data: form,contentType: false, // 必須false才會自動加上正確的Content-TypeprocessData: false, // 必須false才會避開jQuery對 formdata 的默認處理。XMLHttpRequest會對 formdata 進行正確的處理xhr: function () {let xhr = $.ajaxSettings.xhr();xhr.upload.addEventListener("progress", progressSFunction, false);xhr.upload.onloadstart = (e) => {progress[0] = {last_laoded: 0,last_time: e.timeStamp,};console.log("開始上傳",progress);};xhr.upload.onloadend = () => {delete progress[0];console.log("結束上傳",progress);};return xhr;},success: function (data) {data = JSON.parse(data);data.forEach((i) => {console.log(i.code, i.file_url);});},error: function () {alert("aaa上傳失敗!");},});}// jQuery版本進度條function Progressbar(e) {var bar = $("#progressBar"); // 進度條var num = $("#percentage");  // 百分比if (e.lengthComputable) {bar.attr("max", e.total);bar.attr("value", e.loaded);num.text(Math.round(e.loaded / e.total * 100) + "%");}}// 原生js版 并行進度條function progressPFunction(e, k) {var progressBar = document.getElementById(`pro${k}`);var percentageDiv = document.getElementById(`per${k}`);if (e.lengthComputable) {progressBar.max = e.total;progressBar.value = e.loaded;let speed = (e.loaded - progress[k].last_laoded) / (e.timeStamp - progress[k].last_time) + " bytes/s";let percent = Math.round(e.loaded / e.total * 100) + "%";progress[k].last_laoded = e.loaded, progress[k].last_time = e.timeStamp;percentageDiv.innerHTML = percent + " " + speed;console.log(speed);}}// 原生js 串行進度條function progressSFunction(e) {var progressBar = document.getElementById(`pro`);var percentageDiv = document.getElementById(`per`);if (e.lengthComputable) // lengthComputable表示進度信息是否可用{progressBar.max = e.total;progressBar.value = e.loaded;let speed = (e.loaded - progress[0].last_laoded) / (e.timeStamp - progress[0].last_time) + " bytes/s";let percent = Math.round(e.loaded / e.total * 100) + "%";progress[0].last_laoded = e.loaded, progress[0].last_time = e.timeStamp;percentageDiv.innerHTML = percent + " " + speed;}}

Django后端

def file_upload(request):"ajax文件上傳功能"resList, fileList = [], request.FILES.getlist("file")dir_path = 'static/files/{0}/{1}/{2}'.format(time.strftime("%Y"),time.strftime("%m"),time.strftime("%d"))if os.path.exists(dir_path) is False:os.makedirs(dir_path)for file in fileList:file_path = '%s/%s' % (dir_path, file.name)file_url = '/%s/%s' % (dir_path, file.name)res = {"code": 0, "file_url": ""}with open(file_path, 'wb') as f:if f == False:res['code'] = 1for chunk in file.chunks(): # chunks()代替read(),如果文件很大,可以保證不會拖慢系統內存f.write(chunk)res['file_url'] = file_urlresList.append(res)return HttpResponse(json.dumps(resList))

參考:

https://www.cnblogs.com/potatog/p/9342448.html

https://www.w3cmm.com/ajax/progress-events.html

?

轉載于:https://www.cnblogs.com/V587Chinese/p/11371380.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247125.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247125.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247125.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

NSURLSession的應用

iOS7以后發布了NSURLSession用來替換NSURLConnection&#xff0c;NSURLSession使用方式有以下兩種&#xff1a; 1.block方式 &#xff08;1&#xff09;創建的步驟 獲取單例會話對象 創建URL對象 隱含創建request 創建NSURLSessionDataTask // 1.獲取會話對象 NSURLSess…

ASP.NET Core Web 應用程序系列(一)- 使用ASP.NET Core內置的IoC容器DI進行批量依賴注入(MVC當中應用)...

在正式進入主題之前我們來看下幾個概念&#xff1a; 一、依賴倒置 依賴倒置是編程五大原則之一&#xff0c;即&#xff1a; 1、上層模塊不應該依賴于下層模塊&#xff0c;它們共同依賴于一個抽象。 2、抽象不能依賴于具體&#xff0c;具體依賴于抽象。 其中上層就是指使用者&am…

iOS中XML解析

iOS中XML解析分為兩種實現方式&#xff1a;SAX與DOM SAX方式&#xff1a;主要是事件驅動的解析方式&#xff0c;是逐行讀取XML數據&#xff0c;不斷回調代理&#xff0c;告訴代理當前解析的元素開始或者結束。 DOM解析方式&#xff1a;是講整個XML數據全部讀入內存&#xff0…

蘋果電腦基本設置+Linux 命令+Android 實戰集錦

本文微信公眾號「AndroidTraveler」首發。 背景 大多數應屆畢業生在大學期間使用的比較多的是 windows 電腦&#xff0c;因此初入職場如果拿到一臺蘋果電腦&#xff0c;可能一時間不能夠很快的上手。基于此&#xff0c;這邊出了系列視頻&#xff0c;通過實際的演示讓沒使用過蘋…

iOS中POST請求

iOS中POST請求的發送需要使用NSMutableURLRequest可以設置URL request的頭字段&#xff0c;比如超時時間&#xff0c;請求類型&#xff1a;GET POST等一些關鍵頭字段&#xff1a; - (IBAction)login { // 1.用戶名 NSString *usernameText self.username.text; if (userna…

發送JSON數據給服務器

需要將JSON格式的數據傳送給服務器&#xff0c;注意需要設置&#xff1a; [request setValue:”application/json” forHTTPHeaderField:”Content-Type”]; Content-Type類型為&#xff1a;application/json // 1.URL NSURL *url [NSURL URLWithString:"http://localh…

Mac中AndroidStudio沒有找到Plugins的問題

我們在windows中都可以正常找到plugins 但是在Mac上AndroidStudio里 setting打開卻沒有plugins 正準備在Mac上搞一下flutter呢 我感覺智商受到了侮辱&#xff01; 這里其實是mac版本給我開了個玩笑 你可以按快捷鍵&#xff0c;你就可以找到 快捷鍵 command ‘,’ 沒錯就是comm…

進程和操作系統概述

進程和操作系統概述 進程的基礎 程序和進程&#xff1a; 程序是一對靜態的代碼文件 進程是一個正在運行著的程序&#xff0c;抽象概念 進程由操作系統操控調用交于CPU運行 操作系統 1.管理控制協調計算機硬件和軟件的關系 2.操作系統的作用&#xff1f; ? 第一個作用&#xff…

iOS手勢操作簡介(一)

iOS中能夠響應手勢操作的類必須要繼承自UIResponder&#xff0c;才能夠處理手勢響應操作。 默認繼承了UIResponder的類有&#xff1a;UIApplication UIViewController UIView都繼承自UIResponder. UIView是UIResponder的子類&#xff0c;可以實現下列4個方法處理不同的觸摸事…

iOS開發中手勢處理簡介(二)

iOS中手勢操作事件的產生于傳遞 發生觸摸事件后&#xff0c;系統會將該事件加入到一個由UIApplication管理的事件隊列中 UIApplication會從事件隊列中取出最前面的事件&#xff0c;并將事件分發下去以便處理&#xff0c;通常&#xff0c;先發送事件給應用程序的主窗口&#x…

對前端Jenkins自動化部署的研究

1. 安裝 安裝 Nginx 1.1去官網下直接下載&#xff0c;解壓縮 start nginx就可以使了&#xff0c;常用命令&#xff1a; start nginx # 啟動 nginx -s reload # 修改配置后重新加載生效 nginx -s reopen # 重新打開日志文件 nginx -t # 配置文件檢測是否正確 1.2 安裝Jenkins…

python超神之路:Python3 列表list合并的4種方法

Python3 列表list合并的4種方法 方法1: 直接使用""號合并列表 aList [1,2,3] bList [www, pythontab.com] cList aList bList dList bList aList print(cList) print(dList) # 結果&#xff1a; [1, 2, 3, www, pythontab.com] [www, pythontab.com, 1, 2, 3] …

iOS手勢操作簡介(三)

監聽觸摸事件的做法 如果想監聽一個view上面的觸摸事件&#xff0c;之前的做法是 自定義一個view 實現view的touches方法&#xff0c;在方法內部實現具體處理代碼 通過touches方法監聽view觸摸事件&#xff0c;有很明顯的幾個缺點 必須得自定義view 由于是在view內部的to…

iOS手勢操作簡介(四)

當事件傳遞到相應的UIResponder后&#xff0c;會首先調用&#xff1a; hitTest:withEvent: return (UIView *) UIApplication -> UIWindow 什么時候調用&#xff1a;當事件傳遞給一個控件的時候就會調用 作用&#xff1a;找最合適的viewhitTest:withEvent: return (UIView…

ASP.NET Core Web 應用程序系列(二)- 在ASP.NET Core中使用Autofac替換自帶DI進行批量依賴注入(MVC當中應用)...

在上一章中主要和大家分享在MVC當中如何使用ASP.NET Core內置的DI進行批量依賴注入&#xff0c;本章將繼續和大家分享在ASP.NET Core中如何使用Autofac替換自帶DI進行批量依賴注入。 PS&#xff1a;本章將主要采用構造函數注入的方式&#xff0c;下一章將繼續分享如何使之能夠同…

iOS手勢操作簡介(五)

利用手勢操作實現抽屜效果&#xff1a; 第一步&#xff1a;搭建UI (void)addChildView { // left UIView *leftView [[UIView alloc] initWithFrame:self.view.bounds]; leftView.backgroundColor [UIColor greenColor]; [self.view addSubview:leftView]; _leftView…

Java過濾器與SpringMVC攔截器之間的關系與區別

今天學習和認識了一下&#xff0c;過濾器和SpringMVC的攔截器的區別&#xff0c;學到了不少的東西&#xff0c;以前一直以為攔截器就是過濾器實現的&#xff0c;現在想想還真是一種錯誤啊&#xff0c;而且看的比較粗淺&#xff0c;沒有一個全局而又細致的認識&#xff0c;由于已…

iOS手勢操作簡介(六)

利用UIGestureRecognizer來對手勢進行處理&#xff1a; interface HMViewController () property (weak, nonatomic) IBOutlet UIImageView *imagView; end implementation HMViewController (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup aft…

iOS并行程序開發- GCD NSOperationQueue(1)

import UIKit let imageURLs [“http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg“, “http://adriatic-lines.com/wp-content/uploads/2015/04/canal-of-Venice.jpg“, “http://algoos.com/wp-content/uploads/2015/08/ireland-02.jpg“, “http:…

二次冪權限設計

設置含有的權限如增刪改查減為1,2,4,8,16 如果A包含增刪改這5個權限&#xff0c;那A的值為1247 如果B包含增改查這5個權限&#xff0c;那A的值為14813 如果C包含增刪改查減這5個權限&#xff0c;那A的值為12481631 7二進制為111,13的二進制為1101,31二進制為11111 1二進制為1&a…