整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          最新HTML BroadcastChannel AP

          最新HTML BroadcastChannel API簡介

          TML BroadcastChannel API

          當前瀏覽器中只有Firefox38唯一能支持BroadcastChannel API(在編寫本文的時間點),而Firefox38官方宣稱要到2015年5月份才會發布正式版本。這套新的API將會打開一個新的充滿可能性的世界,解決我們已有的從postMessage API所繼承過來的眾多限制。

          BroadcastChannel API作為WHATWG living HTML標準的一部分可以在這里進行詳細信息查看。

          什么是 BroadcastChannel API?

          BroadcastChannel API 允許同一原始域和用戶代理下的所有窗口,iFrames等進行交互。也就是說,如果用戶打開了同一個網站的的兩個標簽窗口,如果網站內容發生了變化,那么兩個窗口會同時得到更新通知。

          還是不明覺厲?就拿Facebook作為例子吧,假如你現在已經打開了Facebook的一個窗口,但是你此時還沒有登錄,此時你又打開另外一個窗口進行登錄,那么你就可以通知其他窗口/標簽頁去告訴它們一個用戶已經登錄了并請求它們進行相應的頁面更新。

          本質上說BroadcastChannel API 允許我們在我們不使用sockets和timers的情況下同樣可以打造出一個能夠自我感知狀態變化的應用,這對于一個發布/訂閱形式的系統效果尤佳。

          BroadcastChannel API 實戰進行時

          創建一個新的 BroadcastChannel

          創建一個新的BroadcastChannel API 是一個易如反掌的事情。你需要做的僅僅是把通道名稱作為一個參數傳給BroadcastChannel的構造函數然后把它的引用保存到一個變量上面而已。

          <code>let cast=new BroadcastChannel('mychannel');</code>

          發送一個消息通知

          發送一個消息也是一個非常簡單的事情,你只需要引用賦有了BroadcastChannel實例的變量(在本示例中就是上面的cast變量)然后調用其postMessage方法就可以了。

          如果你對其他基于發布/訂閱的系統很熟悉的話,如果你把postMessage這個成員方法稱呼成event emitter也許會更合情合理。

          postMessage方法做的漂亮的地方是你可以用它來發送任何東西。你可以發送一個對象,一個字串,隨你便。只要訂閱者可以意識到你要發送的是什么事件就行了,好好享受吧。

          <code>myObj={someKey: 'Some value', anotherKey: 'Another value'};
          cast.postMessage(myObj);</code>

          不像一些更加小鮮肉級別的發布/訂閱系統,“主題“是沒有原生的實現支持的。意思就是說你并沒有一個通道可以把“主題“廣播到所有監聽的訂閱者手上。

          但是,通過編寫一些創造性的代碼你還是可以模仿這種實現的,你可以使用對象來把“主題“作為對象的一個鍵,把消息內容作為另外一個鍵”data”來進行發送。

          消息監聽

          “接收者“更通俗的叫法也許應該叫做”訂閱者”。一個接收者會對如我們前面為一個特別通道所定義的BroadcastChannel通道引用變量所發射的事件進行消息監聽。

          <code>cast.onmessage=<span>function (e) {</span> 
              console.log(e); // This should print out the contents of the object we sent above
          }</code>

          關閉連接

          假定你非常在意Javascript的性能且很介意資源消耗情況(特別是在使用手機的情況下)。 幸運的是在BroadcastChannel里面擁有一個內嵌的方法來讓你關閉這些連接。

          <code>cast.close; // Close our connection and let the garbage collection free up the memory that was used</code>

          瀏覽器支持

          如前所述,當前只有Firefox版本38會支持BroadcastChannel API。假定其他瀏覽器將很有可能也會緊緊追隨的話,畢竟,這套API是非常有用的。那么對我們編寫這方面的普通應用和游戲應用,我們將會如魚得水(緊密的瀏覽器支持的出現)

          要注意的是,從一個使用者的角度來看的話你(當前)是基本上好不到相關的技術支持的。但,這里有個瀏覽器插件可以讓你現在就使用上BroadcastChannel API,然而,畢竟現在還沒有瀏覽器真正實現該功能的支持,我們只有翹首以待了。

          作者:天地會珠海分舵

          微信公眾號:TechGoGoGo

          開發分布式高并發系統時有三把利器用來保護系統:緩存、降級、限流

          緩存

          緩存的目的是提升系統訪問速度和增大系統處理容量

          降級

          降級是當服務出現問題或者影響到核心流程時,需要暫時屏蔽掉,待高峰或者問題解決后再打開

          限流

          限流的目的是通過對并發訪問/請求進行限速,或者對一個時間窗口內的請求進行限速來保護系統,一旦達到限制速率則可以拒絕服務、排隊或等待、降級等處理

          問題描述

          1、某天A君突然發現自己的接口請求量突然漲到之前的10倍,沒多久該接口幾乎不可使用,并引發連鎖反應導致整個系統崩潰。如何應對這種情況呢?生活給了我們答案:比如老式電閘都安裝了保險絲,一旦有人使用超大功率的設備,保險絲就會燒斷以保護各個電器不被強電流給燒壞。同理我們的接口也需要安裝上“保險絲”,以防止非預期的請求對系統壓力過大而引起的系統癱瘓,當流量過大時,可以采取拒絕或者引流等機制。整編:微信公眾號,搜云庫技術團隊,ID:souyunku

          2、緩存的目的是提升系統訪問速度和增大系統能處理的容量,可謂是抗高并發流量的銀彈;而降級是當服務出問題或者影響到核心流程的性能則需要暫時屏蔽掉,待高峰或者問題解決后再打開;而有些場景并不能用緩存和降級來解決,比如稀缺資源(秒殺、搶購)、寫服務(如評論、下單)、頻繁的復雜查詢(評論的最后幾頁),因此需有一種手段來限制這些場景的并發/請求量,即限流。

          3、系統在設計之初就會有一個預估容量,長時間超過系統能承受的TPS/QPS閾值,系統可能會被壓垮,最終導致整個服務不夠用。為了避免這種情況,我們就需要對接口請求進行限流。

          4、限流的目的是通過對并發訪問請求進行限速或者一個時間窗口內的的請求數量進行限速來保護系統,一旦達到限制速率則可以拒絕服務、排隊或等待。

          5、一般開發高并發系統常見的限流模式有控制并發和控制速率,一個是限制并發的總數量(比如數據庫連接池、線程池),一個是限制并發訪問的速率(如nginx的limitconn模塊,用來限制瞬時并發連接數),另外還可以限制單位時間窗口內的請求數量(如Guava的RateLimiter、nginx的limitreq模塊,限制每秒的平均速率)。其他還有如限制遠程接口調用速率、限制MQ的消費速率。另外還可以根據網絡連接數、網絡流量、CPU或內存負載等來限流。

          相關概念:

          PV:

          page view 頁面總訪問量,每刷新一次記錄一次。

          UV:

          unique view 客戶端主機訪問,指一天內相同IP的訪問記為1次。

          QPS:

          query per second,即每秒訪問量。qps很大程度上代表了系統的繁忙度,沒次請求可能存在多次的磁盤io,網絡請求,多個cpu時間片,一旦qps超過了預先設置的閥值,可以考量擴容增加服務器,避免訪問量過大導致的宕機。整編:微信公眾號,搜云庫技術團隊,ID:souyunku

          RT:

          response time,每次請求的響應時間,直接決定用戶體驗性。

          本文主要介紹應用級限流方法,分布式限流、流量入口限流(接入層如NGINX limitconn和limitreq 模塊)。

          應用級限流

          一、控制并發數量

          屬于一種較常見的限流手段,在實際應用中可以通過信號量機制(如Java中的Semaphore)來實現。操作系統的信號量是個很重要的概念,Java 并發庫 的Semaphore 可以很輕松完成信號量控制,Semaphore可以控制某個資源可被同時訪問的個數,通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。

          舉個例子,我們對外提供一個服務接口,允許最大并發數為10,代碼實現如下:

          public class DubboService {
              private final Semaphore permit=new Semaphore(10, true);
              public void process(){
                  try{            permit.acquire();            //業務邏輯處理
                  } catch (InterruptedException e) {            e.printStackTrace();        } finally {            permit.release();        }    }}

          在以上代碼中,雖然有30個線程在執行,但是只允許10個并發的執行。Semaphore的構造方法Semaphore(int permits) 接受一個整型的數字,表示可用的許可證數量。Semaphore(10)表示允許10個線程獲取許可證,也就是最大并發數是10。Semaphore的用法也很簡單,首先線程使用Semaphore的acquire()獲取一個許可證,使用完之后調用release()歸還許可證,還可以用tryAcquire()方法嘗試獲取許可證,信號量的本質是控制某個資源可被同時訪問的個數,在一定程度上可以控制某資源的訪問頻率,但不能精確控制,控制訪問頻率的模式見下文描述。

          二、控制訪問速率

          在工程實踐中,常見的是使用令牌桶算法來實現這種模式,常用的限流算法有兩種:漏桶算法和令牌桶算法。

          漏桶算法

          漏桶算法思路很簡單,水(請求)先進入到漏桶里,漏桶以一定的速度出水,當水流入速度過大會直接溢出,可以看出漏桶算法能強行限制數據的傳輸速率。

          對于很多應用場景來說,除了要求能夠限制數據的平均傳輸速率外,還要求允許某種程度的突發傳輸。這時候漏桶算法可能就不合適了,令牌桶算法更為適合。

          令牌桶算法

          如圖所示,令牌桶算法的原理是系統會以一個恒定的速度往桶里放入令牌,而如果請求需要被處理,則需要先從桶里獲取一個令牌,當桶里沒有令牌可取時,則拒絕服務,令牌桶算法通過發放令牌,根據令牌的rate頻率做請求頻率限制,容量限制等。整編:微信公眾號,搜云庫技術團隊,ID:souyunku

          在Wikipedia上,令牌桶算法是這么描述的:

          1、每過1/r秒桶中增加一個令牌。

          2、桶中最多存放b個令牌,如果桶滿了,新放入的令牌會被丟棄。

          3、當一個n字節的數據包到達時,消耗n個令牌,然后發送該數據包。

          4、如果桶中可用令牌小于n,則該數據包將被緩存或丟棄。

          令牌桶控制的是一個時間窗口內通過的數據量,在API層面我們常說的QPS、TPS,正好是一個時間窗口內的請求量或者事務量,只不過時間窗口限定在1s罷了。以一個恒定的速度往桶里放入令牌,而如果請求需要被處理,則需要先從桶里獲取一個令牌,當桶里沒有令牌可取時,則拒絕服務。令牌桶的另外一個好處是可以方便的改變速度,一旦需要提高速率,則按需提高放入桶中的令牌的速率。

          在我們的工程實踐中,通常使用Google開源工具包Guava提供的限流工具類RateLimiter來實現控制速率,該類基于令牌桶算法來完成限流,非常易于使用,而且非常高效。如我們不希望每秒的任務提交超過1個

          public static void main(String[] args) {    String start=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());    RateLimiter limiter=RateLimiter.create(1.0); // 這里的1表示每秒允許處理的量為1個    for (int i=1; i <=10; i++) {        double waitTime=limiter.acquire(i); // 請求RateLimiter, 超過permits會被阻塞        System.out.println("cutTime=" + System.currentTimeMillis() + " call execute:" + i + " waitTime:" + waitTime);    }    String end=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());    System.out.println("start time:" + start);    System.out.println("end time:" + end);}

          首先通過RateLimiter.create(1.0);創建一個限流器,參數代表每秒生成的令牌數,通過limiter.acquire(i);來以阻塞的方式獲取令牌,令牌桶算法允許一定程度的突發(允許消費未來的令牌),所以可以一次性消費i個令牌;當然也可以通過tryAcquire(int permits, long timeout, TimeUnit unit)來設置等待超時時間的方式獲取令牌,如果超timeout為0,則代表非阻塞,獲取不到立即返回,支持阻塞或可超時的令牌消費。

          從輸出來看,RateLimiter支持預消費,比如在acquire(5)時,等待時間是4秒,是上一個獲取令牌時預消費了3個兩排,固需要等待3*1秒,然后又預消費了5個令牌,以此類推。

          RateLimiter通過限制后面請求的等待時間,來支持一定程度的突發請求(預消費),在使用過程中需要注意這一點,Guava有兩種限流模式,一種為穩定模式(SmoothBursty:令牌生成速度恒定,平滑突發限流),一種為漸進模式(SmoothWarmingUp:令牌生成速度緩慢提升直到維持在一個穩定值,平滑預熱限流) 兩種模式實現思路類似,主要區別在等待時間的計算上。

          SmoothBursty 模式:

          RateLimiter limiter=RateLimiter.create(5); RateLimiter.create(5)表示桶容量為5且每秒新增5個令牌,即每隔200毫秒新增一個令牌;limiter.acquire()表示消費一個令牌,如果當前桶中有足夠令牌則成功(返回值為0),如果桶中沒有令牌則暫停一段時間,比如發令牌間隔是200毫秒,則等待200毫秒后再去消費令牌,這種實現將突發請求速率平均為了固定請求速率。

          SmoothWarmingUp模式:

          RateLimiter limiter=RateLimiter.create(5,1000, TimeUnit.MILLISECONDS);

          創建方式:

          RateLimiter.create(doublepermitsPerSecond, long warmupPeriod, TimeUnit unit),permitsPerSecond表示每秒新增的令牌數,warmupPeriod表示在從冷啟動速率過渡到平均速率的時間間隔。速率是梯形上升速率的,也就是說冷啟動時會以一個比較大的速率慢慢到平均速率;然后趨于平均速率(梯形下降到平均速率)。可以通過調節warmupPeriod參數實現一開始就是平滑固定速率。整編:微信公眾號,搜云庫技術團隊,ID:souyunku

          放在Controller中用Jemter壓測

          注:RateLimiter控制的是速率,Samephore控制的是并發量。RateLimiter的原理就是令牌桶,它主要由許可發出的速率來定義,如果沒有額外的配置,許可證將按每秒許可證規定的固定速度分配,許可將被平滑地分發,若請求超過permitsPerSecond則RateLimiter按照每秒 1/permitsPerSecond 的速率釋放許可。注意:RateLimiter適用于單體應用,且RateLimiter不保證公平性訪問。

          使用上述方式使用RateLimiter的方式不夠優雅,自定義注解+AOP的方式實現(適用于單體應用),詳細見下面代碼:

          自定義注解:

          import java.lang.annotation.*;
          /** * 自定義注解可以不包含屬性,成為一個標識注解 */@Inherited@Documented@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface RateLimitAspect {
          }

          自定義切面類

          import com.google.common.util.concurrent.RateLimiter;import com.test.cn.springbootdemo.util.ResultUtil;import net.sf.json.JSONObject;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;
          import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.IOException;
          @Component@Scope@Aspectpublic class RateLimitAop {
              @Autowired    private HttpServletResponse response;
              private RateLimiter rateLimiter=RateLimiter.create(5.0); //比如說,我這里設置"并發數"為5
              @Pointcut("@annotation(com.test.cn.springbootdemo.aspect.RateLimitAspect)")    public void serviceLimit() {
              }
              @Around("serviceLimit()")    public Object around(ProceedingJoinPoint joinPoint) {        Boolean flag=rateLimiter.tryAcquire();        Object obj=null;        try {            if (flag) {                obj=joinPoint.proceed();            }else{                String result=JSONObject.fromObject(ResultUtil.success1(100, "failure")).toString();                output(response, result);            }        } catch (Throwable e) {            e.printStackTrace();        }        System.out.println("flag=" + flag + ",obj=" + obj);        return obj;    }
              public void output(HttpServletResponse response, String msg) throws IOException {        response.setContentType("application/json;charset=UTF-8");        ServletOutputStream outputStream=null;        try {            outputStream=response.getOutputStream();            outputStream.write(msg.getBytes("UTF-8"));        } catch (IOException e) {            e.printStackTrace();        } finally {            outputStream.flush();            outputStream.close();        }    }}

          測試controller

          import com.test.cn.springbootdemo.aspect.RateLimitAspect;import com.test.cn.springbootdemo.util.ResultUtil;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;
          @Controllerpublic class TestController {
              @ResponseBody    @RateLimitAspect    @RequestMapping("/test")    public String test(){        return ResultUtil.success1(1001, "success").toString();    }

          壓測結果:

          三、控制單位時間窗口內請求數

          某些場景下,我們想限制某個接口或服務 每秒/每分鐘/每天 的請求次數或調用次數。例如限制服務每秒的調用次數為50,實現如下:

          private LoadingCache < Long, AtomicLong > counter=CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build(new CacheLoader < Long, AtomicLong > () {@    Override    public AtomicLong load(Long seconds) throws Exception {        return new AtomicLong(0);    }});public static long permit=50;public ResponseEntity getData() throws ExecutionException {    //得到當前秒    long currentSeconds=System.currentTimeMillis() / 1000;    if (counter.get(currentSeconds).incrementAndGet() > permit) {        return ResponseEntity.builder().code(404).msg("訪問速率過快").build();    }    //業務處理}

          到此應用級限流的一些方法就介紹完了。假設將應用部署到多臺機器,應用級限流方式只是單應用內的請求限流,不能進行全局限流。因此我們需要分布式限流和接入層限流來解決這個問題。

          分布式限流

          自定義注解+攔截器+Redis實現限流 (單體和分布式均適用,全局限流)

          自定義注解:

          @Inherited@Documented@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface AccessLimit {
              int limit() default 5;  
              int sec() default 5;}

          攔截器:

          public class AccessLimitInterceptor implements HandlerInterceptor {
              @Autowired    private RedisTemplate<String, Integer> redisTemplate;  //使用RedisTemplate操作redis
              @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if (handler instanceof HandlerMethod) {            HandlerMethod handlerMethod=(HandlerMethod) handler;            Method method=handlerMethod.getMethod();            if (!method.isAnnotationPresent(AccessLimit.class)) {                return true;            }            AccessLimit accessLimit=method.getAnnotation(AccessLimit.class);            if (accessLimit==null) {                return true;            }            int limit=accessLimit.limit();            int sec=accessLimit.sec();            String key=IPUtil.getIpAddr(request) + request.getRequestURI();            Integer maxLimit=redisTemplate.opsForValue().get(key);            if (maxLimit==null) {                redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);  //set時一定要加過期時間            } else if (maxLimit < limit) {                redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);            } else {                output(response, "請求太頻繁!");                return false;            }        }        return true;    }
              public void output(HttpServletResponse response, String msg) throws IOException {        response.setContentType("application/json;charset=UTF-8");        ServletOutputStream outputStream=null;        try {            outputStream=response.getOutputStream();            outputStream.write(msg.getBytes("UTF-8"));        } catch (IOException e) {            e.printStackTrace();        } finally {            outputStream.flush();            outputStream.close();        }    }
              @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              }
              @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              }}

          controller:

          @Controller@RequestMapping("/activity")public class AopController {    @ResponseBody    @RequestMapping("/seckill")    @AccessLimit(limit=4,sec=10)  //加上自定義注解即可    public String test (HttpServletRequest request,@RequestParam(value="username",required=false) String userName){        //TODO somethings……        return   "hello world !";    }}

          配置文件:

          /*springmvc的配置文件中加入自定義攔截器*/<mvc:interceptors>   <mvc:interceptor>      <mvc:mapping path="/**"/>      <bean class="com.pptv.activityapi.controller.pointsmall.AccessLimitInterceptor"/>   </mvc:interceptor></mvc:interceptors>

          訪問效果如下,10s內訪問接口超過4次以上就過濾請求,原理和計數器算法類似:

          接入層限流

          主要介紹nginx 限流,采用漏桶算法。

          限制原理:可一句話概括為:“根據客戶端特征,限制其訪問頻率”,客戶端特征主要指IP、UserAgent等。使用IP比UserAgent更可靠,因為IP無法造假,UserAgent可隨意偽造。整編:微信公眾號,搜云庫技術團隊,ID:souyunku

          用limit_req模塊來限制基于IP請求的訪問頻率:

          http://nginx.org/en/docs/http/ngxhttplimitreqmodule.html

          也可以用tengine中的增強版:

          http://tengine.taobao.org/documentcn/httplimitreqcn.html

          1、并發數和連接數控制的配置:

          nginx http配置:    #請求數量控制,每秒20個    limit_req_zone $binary_remote_addr zone=one:10m rate=20r/s;    #并發限制30個    limit_conn_zone $binary_remote_addr zone=addr:10m;
              server塊配置    limit_req zone=one burst=5;    limit_conn addr 30;

          2、ngxhttplimitconnmodule 可以用來限制單個IP的連接數:

          ngxhttplimitconnmodule模塊可以按照定義的鍵限定每個鍵值的連接數。可以設定單一 IP 來源的連接數。

          并不是所有的連接都會被模塊計數;只有那些正在被處理的請求(這些請求的頭信息已被完全讀入)所在的連接才會被計數。

          http {    limit_conn_zone $binary_remote_addr zone=addr:10m;    ...    server {        ...        location /download/ {            limit_conn addr 1;        }

          TML實現文件夾的上傳和下載,前端如何用HTML5實現分片上傳GB級大文件,網頁中實現文件上傳下載的三種解決方案(推薦),HTML5實現文件批量上傳組件,JQUERY 實現文件夾上傳(保留目錄結構),B/S大文件上傳支持斷點上傳,WebService 大文件上傳,斷點續傳分片,HTML+AJAX實現上傳大文件問題,用HTML實現本地文件的上傳,HTML5實現大文件上傳,HTML5實現大文件分片上傳思路,利用HTML5分片上傳超大文件思路,

          WEBUPLOADER 支持 超大上G,多附件上傳,JS 大文件分割/分片上傳,

          百度WEBUPLOADER上傳視頻等大文件,WEBUPLOAD組件實現文件上傳功能和下載功能,JS大文件切片上傳,斷點續傳實現DEMO,前端上傳大文件的解決方案,前端上傳大文件處理(切片、斷點續傳),前端大文件上傳優化方案——分片上傳,vue大文件上傳解決方案,vue大文件上傳解決方案10G,vue大文件上傳解決方案50G,vue大文件上傳解決方案100G,html5如何實現大文件斷點續傳、秒傳,

          java如何實現大文件斷點續傳、秒傳,SpringBoot如何實現大文件斷點續傳、秒傳,SpringMVC如何實現大文件斷點續傳、秒傳,SpringCloud如何實現大文件斷點續傳、秒傳,

          webuploader如何實現大文件斷點續傳、秒傳,百度webuploader如何實現大文件斷點續傳、秒傳,html5實現大文件斷點續傳、秒傳,vue如何實現大文件斷點續傳、秒傳,前端如何實現大文件斷點續傳、秒傳,JavaScript如何實現大文件斷點續傳、秒傳,

          html5大文件斷點續傳、秒傳解決方案,html5大文件斷點續傳、加密上傳解決方案,html5大文件斷點續傳、加密存儲解決方案,html5大文件斷點續傳分片解決方案,html5大文件斷點續傳分塊解決方案,html5大文件斷點續傳分割解決方案,html5大文件斷點續傳切割解決方案,

          后端我們公司項目組選的是JAVA,因為公司有自己的產品,所以直接使用就行了,針對客戶需求這塊是進行擴展。

          客戶這邊實際上要傳的文件單個大小就有50G左右,所以需要支持斷點續傳和分片上傳,并且要支持多線程上傳,能夠充分利用帶寬資源。

          之前在網上找過相關的資料,論壇里面也有網友交流過,還加過很多QQ群和微信群,但是結果都不太令人滿意。

          技術選型的話用的是jquery,也是一個企業內網系統,用的是之前公司的框架,只是進行功能擴展

          分片網上討論的很多,基本上全部都是用的HTML5的API,這個方案也不是不能用,但是在IE下面就不行了,兼容性差了點,并且也不能進行擴展,不能進行二次開發,限制性非常大,我們技術同事提的要求是需要提供產品完整源代碼,

          網上的文章全部都沒有提供文件夾上傳和續傳的代碼,也沒有提供數據庫操作的代碼,

          另外這塊我們是要求必須提供產品完整源代碼,因為后面我們需要自已來維護,同時是要求能夠自主可控的

          研發部門的同事調研過百度的webuploader這個組件,發現他實際上就是對Flash和HTML5進行了一個封裝,本質還是調的HTML5的API,之前在項目中也用過,嘗試過,但是最終都不太滿意,一個是兼容性非常差,說的是兼容IE,但是在IE用的是Flash,在很多用戶的電腦上用不了,卡頓崩潰發生的太頻繁,文件上傳的數量一多比如幾千個,前端頁面就開始卡了,用戶體驗非常差。這些問題研發部的同事都向百度反應過,但是百度webuploader那邊一直沒人回,基本上沒人管,領導說要求付費提供技術支持,那邊也是沒人回,聯系不上他們任何人。

          webuploader這邊連個開發人員都聯系不到,這個是怎么回事?

          用戶上傳的文件比較大,有20G左右,直接用HTML傳的話容易失敗,服務器也容易出錯,需要分片,分塊,分割上傳。也就是將一個大的文件分成若干個小文件塊來上傳,另外就是需要實現秒傳功能和防重復功能,秒傳就是用戶如果上傳過這個文件,那么直接在數據庫中查找記錄就行了,不用再上傳一次,節省時間,實現的思路是對文件做MD5計算,將MD5值保存到數據庫,算法可以用MD5,或者CRC,或者SHA1,這個隨便哪個算法都行。

          切片的話還有一點就是在服務器上合并,一個文件的所有分片數據上傳完后需要在服務器端進行合并操作。

          視頻教程:https://www.ixigua.com/7227314770696012322

          導入項目:
          導入到Eclipse:http://www.ncmem.com/doc/view.aspx?id=9da9c7c2b91b40b7b09768eeb282e647
          導入到IDEA:http://www.ncmem.com/doc/view.aspx?id=9fee385dfc0742448b56679420f22162
          springboot統一配置:http://www.ncmem.com/doc/view.aspx?id=7768eec9284b48e3abe08f032f554ea2

          下載示例:

          https://gitee.com/xproer/up6-jsp-eclipse/tree/6.5.40/

          工程

          NOSQL

          NOSQL示例不需要任何配置,可以直接訪問測試

          創建數據表

          選擇對應的數據表腳本,這里以SQL為例

          修改數據庫連接信息

          訪問頁面進行測試

          文件存儲路徑

          up6/upload/年/月/日/guid/filename

          相關問題:
          1.javax.servlet.http.HttpServlet錯誤
          2.項目無法發布到tomcat
          3.md5計算完畢后卡住
          4.服務器找不到config.json文件

          相關參考:

          文件保存位置

          源碼工程文檔:https://drive.weixin.qq.com/s?k=ACoAYgezAAw1dWofra

          源碼報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwoiul8gl

          OEM版報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwuzp4W0a

          產品源代碼:https://drive.weixin.qq.com/s?k=ACoAYgezAAwbdKCskc
          授權生成器:https://drive.weixin.qq.com/s?k=ACoAYgezAAwTIcFph1


          主站蜘蛛池模板: 中文字幕精品一区二区日本| 欧美日韩综合一区二区三区| 在线观看国产一区| 在线播放精品一区二区啪视频| 日本一区二区三区在线网| 无码国产精品一区二区免费16| 亚洲AⅤ无码一区二区三区在线 | 久久久老熟女一区二区三区| 精品一区二区三区无码免费直播 | 性盈盈影院免费视频观看在线一区 | 国产在线一区二区综合免费视频| 亚洲av成人一区二区三区 | 一区二区三区午夜视频| 免费无码一区二区| 久久精品国内一区二区三区| 亚洲天堂一区二区| 亚洲一区二区三区电影| av无码人妻一区二区三区牛牛 | 天堂一区人妻无码| 亚洲色偷偷偷网站色偷一区| 国产伦理一区二区| 国产精品视频第一区二区三区| 亚洲国产综合无码一区二区二三区 | 视频一区二区在线观看| 国产精品视频一区| 国产成人精品亚洲一区| 性色av无码免费一区二区三区 | 精品女同一区二区三区免费站| 久久精品视频一区| 亚洲午夜精品一区二区| 高清一区二区三区日本久| 亚洲一区二区三区日本久久九| 国产凹凸在线一区二区| 国产成人久久精品一区二区三区| 一区二区乱子伦在线播放| 久久国产午夜一区二区福利| 精品一区二区三区在线观看视频 | 亚洲一区二区三区在线网站| 人妻无码视频一区二区三区 | 久久国产高清一区二区三区| 午夜无码视频一区二区三区|