1. 程式人生 > >關於使用阿里雲OSS物件儲存上傳大視訊和轉碼的一些心得

關於使用阿里雲OSS物件儲存上傳大視訊和轉碼的一些心得


這裡寫程式碼片
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSErrorCode;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.ObjectMetadata;

/**
* @Description: 使用普通方式上傳小檔案,使用Multipart上傳方式進行多執行緒分段上傳較大檔案
* @author: zrk
* @time: 2015年3月30日 上午10:45:12
*/
public class OSSUploadFile implements Callable{
private static final Logger logger = LoggerFactory
.getLogger(OSSUploadFile.class);

//外層執行緒池
public static ExecutorService uploadMainPool = null;
static{
    uploadMainPool = Executors.newFixedThreadPool(Constant.CONCURRENT_FILE_NUMBER,new ThreadFactory() {
        public Thread newThread(Runnable r) {
            Thread s = Executors.defaultThreadFactory().newThread(r);
            s.setDaemon(true);
            return s;
        }
    });
}
//內層執行緒池
private ExecutorService pool ;
private MultipartFile sourcePath;//原檔案路徑
private String bucketName;//bucketName
private String key;//雲端儲存路徑
/**
 * oss上傳 支援斷點續傳
 * @param sourcePath    原始檔路徑
 * @param bucketName    bucketName
 * @param key           儲存key  -在oss的儲存路徑
 */
public OSSUploadFile(MultipartFile sourcePath,String bucketName,String key) {
    //例項化單檔案上次執行緒池
    pool = Executors.newFixedThreadPool(Constant.SINGLE_FILE_CONCURRENT_THREADS);
    this.sourcePath = sourcePath;
    this.bucketName = bucketName;
    this.key = key;
}

/**
 * 執行當前執行緒
 * @return
 */
@SuppressWarnings("finally")
public Integer uploadFile() {
    Integer r = Global.ERROR;
     //向uploadMainPool中submit當前執行緒
    Future<Integer> result = uploadMainPool.submit(this);
    try {
        r=result.get();
    } catch (ExecutionException e) {
        e.printStackTrace();
    } finally{
        return r;
    }

}


/**
 * oss上傳
 * @param sourcePath    原始檔路徑
 * @param bucketName    bucketName
 * @param key           儲存key  儲存路徑
 * @return Integer
 */
@Override
public Integer call(){

    OSSClient client  = OssUtil.getOSSClient();    

    //File uploadFile = new File(sourcePath);
    /*if (!sourcePath.exists()){ 

        return Global.FILE_NOT_FOUND_ERROR;
    }*/
    int result = Global.ERROR;

    key = key.contains("\\\\")?key.replaceAll("\\\\", "/"):key.contains("\\")?key.replaceAll("\\", "/"):key; 
    // 準備Bucket
    result = ensureBucket(client,bucketName);
    if(result == Global.ERROR )return result;
    // 使用multipart的方式上傳檔案
    result = uploadBigFile(client, bucketName, key, sourcePath);
    pool = null;
    return result;
}


// 通過Multipart的方式上傳一個大檔案
private int uploadBigFile(OSSClient client, String bucketName, String key,
        MultipartFile uploadFile)  {

    //自定義的每個上傳分塊大小
    Integer partSize = Constant.UPLOAD_PART_SIZE;

    //需要上傳的檔案分塊數
    int partCount = calPartCount(uploadFile,partSize);

    //檔案的MD5值
    String fileDM5Str = "";
    String uploadId = "";

    //序列化的檔案路徑(與上傳檔案同路徑使用.up.temp字尾)

    String serializationFilePath =File.separator+ "tmp"+File.separator+"vide.mp4"+".up.temp";

    boolean isSerializationFile = false;

    //子執行緒池的執行緒物件封裝類(用於序列化的)
    UploadPartObj uploadPartObj = null;
    //獲取檔案MD5值
    fileDM5Str = MD5Util.getFileMD5(uploadFile);

    //若存在上傳失敗留下的序列化檔案則反序列化物件
    if(new File(serializationFilePath).exists()){
        uploadPartObj = (UploadPartObj)ObjectSerializableUtil.deserialization(serializationFilePath);
        isSerializationFile = true;
    }       

    //序列化檔案不存在,分配分塊給子執行緒池執行緒物件
    if(uploadPartObj==null||!isSerializationFile){
        uploadPartObj = new UploadPartObj();
        try {
            //初始化MultipartUpload 返回uploadId  
            uploadId = initMultipartUpload(client, bucketName, key,fileDM5Str);
        } catch (Exception e) {
            e.printStackTrace();

            return Global.OSS_SUBMIT_ERROR;
        } 
        for (int i = 0; i < partCount ; i++) {
            long start = partSize * i;
            long curPartSize = partSize < uploadFile.getSize() - start ?
                    partSize : uploadFile.getSize() - start;
          //構造上傳執行緒,UploadPartThread是執行每個分塊上傳任務的執行緒

         uploadPartObj.getUploadPartThreads().add(new UploadPartThread(client, bucketName, key,uploadFile, uploadId, i + 1,partSize * i, curPartSize));
        }
    }

    try {
        int i = 0;
        //upload方法提交分塊上傳執行緒至子執行緒池上傳,while迴圈用於上傳失敗重複上傳,Constant.RETRY定義重複次數
        while (upload(uploadPartObj,serializationFilePath).isResult()==false) {
            if(++i == Constant.RETRY)break;
        }
    } catch (Exception e) {
        e.printStackTrace();

        return Global.THREAD_ERROR;
    } 

    if(!uploadPartObj.isResult()){
        return Global.NETWORK_ERROR;
    }
    try {
        //完成一個multi-part請求。
        completeMultipartUpload(client, bucketName, key, uploadPartObj);
    } catch (Exception e) {
        e.printStackTrace();
        logger.error("multi-part請求失敗!!");
       ObjectSerializableUtil.serialization(uploadPartObj,serializationFilePath);
        return Global.OSS_SUBMIT_ERROR;
    }
    return Global.SUCCESS;
}

/**
 * 多執行緒上傳單個檔案
 * @param uploadPartObj
 * @param serializationFilePath
 * @return
 */
private UploadPartObj upload(UploadPartObj uploadPartObj,String serializationFilePath){

    try {

        uploadPartObj.setResult(true);

        //向子執行緒池中submit單個檔案所有分塊上傳執行緒
        for (int i=0;i<uploadPartObj.getUploadPartThreads().size();i++) {
            if(uploadPartObj.getUploadPartThreads().get(i).getMyPartETag()==null)
                pool.submit(uploadPartObj.getUploadPartThreads().get(i));
        }

        //shutdown子執行緒池,池內所上傳任務執行結束後停止當前執行緒池
        pool.shutdown();
        while (!pool.isTerminated()) {
            //迴圈檢查執行緒池,同時在此序列化uploadPartObj
            ObjectSerializableUtil.serialization(uploadPartObj,serializationFilePath);
            pool.awaitTermination(Constant.SERIALIZATION_TIME, TimeUnit.SECONDS);
        }

        //判斷上傳結果
        for (UploadPartThread uploadPartThread: uploadPartObj.getUploadPartThreads()) {
            if(uploadPartThread.getMyPartETag()==null)
                uploadPartObj.setResult(false);

        }
        //上傳成功 刪除序列化檔案
        if (uploadPartObj.isResult()==true) 
            ObjectSerializableUtil.delSerlzFile(serializationFilePath);
    } catch (Exception e) {
       logger.error("多執行緒上傳單個檔案異常!!");

    }
    return uploadPartObj;


}

// 根據檔案的大小和每個Part的大小計算需要劃分的Part個數。
private static int calPartCount(MultipartFile f,Integer partSize) {
    int partCount = (int) (f.getSize() / partSize);
    if (f.getSize() % partSize != 0){
        partCount++;
    }
    return partCount;
}

// 建立Bucket
private int ensureBucket(OSSClient client, String bucketName){
        try {
            // 建立bucket
            client.createBucket(bucketName);
            //設定bucket的訪問許可權,public-read-write許可權
            client.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
        } catch (OSSException e) {
            e.printStackTrace();

            return Global.ERROR;
        } catch (ClientException e) {
            if (!OSSErrorCode.BUCKET_ALREADY_EXISTS.equals(e.getErrorCode())) {
                // 如果Bucket已經存在,則忽略

            }else{
                e.printStackTrace();

                return Global.ERROR;
            }
        }
        return Global.SUCCESS;
}

// 初始化一個Multi-part upload請求。
private static String initMultipartUpload(OSSClient client,String bucketName, String key,String fileDM5Str) throws OSSException,ClientException{
    String uploadId=null;
   try{ 
    ObjectMetadata objectMetadata =new ObjectMetadata();
    objectMetadata.getUserMetadata().put(Global.X_OSS_META_MY_MD5,fileDM5Str);
    InitiateMultipartUploadRequest initUploadRequest = new InitiateMultipartUploadRequest(bucketName, key, objectMetadata);
    InitiateMultipartUploadResult initResult = client.initiateMultipartUpload(initUploadRequest);
    uploadId = initResult.getUploadId();

    System.err.println(uploadId);
   }catch (Exception e) {
    logger.error("初始化Multi-part upload請求失敗!!");
}
    return uploadId;

}

// 完成一個multi-part請求。
private static void completeMultipartUpload(OSSClient client, String bucketName, String key,UploadPartObj uploadPartObj){
    List<PartETag> eTags = new ArrayList<PartETag>(); 
    for (UploadPartThread uploadPartThread : uploadPartObj.getUploadPartThreads()) {
        eTags.add(new PartETag(uploadPartThread.getMyPartETag().getPartNumber(),uploadPartThread.getMyPartETag().geteTag()));
    }
    //為part按partnumber排序
    Collections.sort(eTags, new Comparator<PartETag>(){
        public int compare(PartETag arg0, PartETag arg1) {
            PartETag part1= arg0;
            PartETag part2= arg1;
            return part1.getPartNumber() - part2.getPartNumber();
        }  
    });
    try{
    CompleteMultipartUploadRequest completeMultipartUploadRequest =
            new CompleteMultipartUploadRequest(bucketName, key, uploadPartObj.getUploadPartThreads().get(0).getUploadId(), eTags);
    System.err.println(uploadPartObj.getUploadPartThreads().get(0).getUploadId());
    client.completeMultipartUpload(completeMultipartUploadRequest);
    }catch (Exception e) {
        logger.error("合併上傳失敗!!");
    }
}

}

這裡是主程式,用的檔案流是MultipartFile,下面是主程式需要用到的一些類和方法。

import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;

import org.springframework.web.multipart.MultipartFile;

public class MD5Util {
public static String getFileMD5(MultipartFile file) {
/* if (!file.exists() || !file.isFile()) {
return null;
} */
MessageDigest digest = null;
InputStream in = null;
byte buffer[] = new byte[1024];
int len;
try {
digest = MessageDigest.getInstance(“MD5”);
//in = new FileInputStream(file);
in=file.getInputStream();
while ((len = in.read(buffer, 0, 1024)) != -1) {
digest.update(buffer, 0, len);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
BigInteger bigInt = new BigInteger(1, digest.digest());
return bigInt.toString(16);
}

}

import java.io.Serializable;

import com.aliyun.oss.model.PartETag;

/**
* @Description: 封裝PartETag 用於序列化
* @author: zrk
* @time: 2015年4月1日 上午10:52:50
*/
public class MyPartETag implements Serializable {

private static final long serialVersionUID = 1L;

private int partNumber;

private String eTag;


public MyPartETag(PartETag partETag ) {
    super();
    this.partNumber = partETag.getPartNumber();
    this.eTag = partETag.getETag();
}

public int getPartNumber() {
    return partNumber;
}

public void setPartNumber(int partNumber) {
    this.partNumber = partNumber;
}

public String geteTag() {
    return eTag;
}

public void seteTag(String eTag) {
    this.eTag = eTag;
}

}

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectSerializableUtil {
public static void serialization (Object object, String serializationFilePath) {
File file = new File(serializationFilePath);
if (!new File(file.getParent()).exists())
new File(file.getParent()).mkdirs();
if (file.exists())
file.delete();
ObjectOutputStream oos = null;

    try {
        oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(object);
        oos.close();
    } catch (IOException e) {
        e.printStackTrace();

        try {if (oos!=null)oos.close();} catch (IOException e1) {e1.printStackTrace();}
        file.delete();
    }
}

public static Object deserialization (String serializationFilePath) {
    File file = new File(serializationFilePath);
    if (!file.exists())
        return null;
    else {

    }
    try {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Object object = ois.readObject();
        ois.close();
        file.delete();
        return object;
    } catch (Exception e) {

        return null;
    }
}

public static boolean delSerlzFile(String serializationFilePath) {
    File file = new File(serializationFilePath);
    if (file.exists())
        return file.delete();
    return true;
}

}

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* 單個檔案的上傳執行緒集合
* @Description: TODO
* @author: zrk
* @time: 2015年5月6日 下午1:56:54
*/
public class UploadPartObj implements Serializable{

private static final long serialVersionUID = 1L;

List<UploadPartThread> uploadPartThreads = Collections.synchronizedList(new ArrayList<UploadPartThread>());

boolean result = true;

public List<UploadPartThread> getUploadPartThreads() {
    return uploadPartThreads;
}
public void setUploadPartThreads(List<UploadPartThread> uploadPartThreads) {
    this.uploadPartThreads = uploadPartThreads;
}
public boolean isResult() {
    return result;
}
public void setResult(boolean result) {
    this.result = result;
}

}

import java.io.InputStream;
import java.io.Serializable;
import java.util.concurrent.Callable;

import org.springframework.web.multipart.MultipartFile;

import com.aliyun.oss.model.UploadPartResult;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.UploadPartRequest;

/**
* @Description: 上傳每個part的執行緒類 可序列化 用於上傳的斷點續傳
* @author: zrk
* @time: 2015年4月1日 上午10:35:34
*/
public class UploadPartThread implements Callable ,Serializable {

private static final long serialVersionUID = 1L;

private MultipartFile uploadFile;
private String bucket;
private String object;
private long start;
private long size;
private int partId;
private String uploadId;

private MyPartETag myPartETag; 

public UploadPartThread(OSSClient client,String bucket, String object,
        MultipartFile uploadFile,String uploadId, int partId,
        long start, long partSize) {
    this.uploadFile = uploadFile;
    this.bucket = bucket;
    this.object = object;
    this.start = start;
    this.size = partSize; 
    this.partId = partId;
    this.uploadId = uploadId;
}

@SuppressWarnings("finally")
@Override
public UploadPartThread call() {

    InputStream in = null;
    try {
        //in = new FileInputStream(uploadFile);
        in=uploadFile.getInputStream();
        in.skip(start);

        UploadPartRequest uploadPartRequest = new UploadPartRequest();
        uploadPartRequest.setBucketName(bucket);
        uploadPartRequest.setKey(object);
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setInputStream(in);
        uploadPartRequest.setPartSize(size);
        uploadPartRequest.setPartNumber(partId);
        //MyPartETag是對uploadPartResult.getPartETag()的返回值PartETag的封裝,主要是為了能序列化PartETag,MyPartETag僅比PartETag多實現了Serializable介面
        UploadPartResult uploadPartResult = OssUtil.getOSSClient().uploadPart(uploadPartRequest); 
        myPartETag = new MyPartETag(uploadPartResult.getPartETag());
    } catch (Exception e) {
        e.printStackTrace();

    } finally {
        if (in != null){
            try {
                in.close();
            } catch (final Exception e) {

            }
        }
        return this;
    }
}

public String getUploadId() {
    return uploadId;
}

public void setUploadId(String uploadId) {
    this.uploadId = uploadId;
}

public MyPartETag getMyPartETag() {
    return myPartETag;
}

public void setMyPartETag(MyPartETag myPartETag) {
    this.myPartETag = myPartETag;
}

}

下面的程式碼是獲取oss物件和簡單上傳小檔案及一個單執行緒的分片斷點續傳。
import java.io.FileInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

import org.apache.commons.lang.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.CompleteMultipartUploadResult;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.ListMultipartUploadsRequest;
import com.aliyun.oss.model.ListPartsRequest;
import com.aliyun.oss.model.MultipartUpload;
import com.aliyun.oss.model.MultipartUploadListing;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.model.PartListing;
import com.aliyun.oss.model.PartSummary;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyun.oss.model.UploadPartRequest;
import com.aliyun.oss.model.UploadPartResult;

public class OssUtil {

// 阿里雲API的內或外網域名
private static String ENDPOINT;
// 阿里雲API的金鑰Access Key ID
private static String ACCESS_KEY_ID;
// 阿里雲API的金鑰Access Key Secret
private static String ACCESS_KEY_SECRET;

// init static datas
static {
    ResourceBundle bundle = PropertyResourceBundle.getBundle("oss");
    ENDPOINT = bundle.getString("endpoint").trim();
    int a = ENDPOINT.length();
    System.err.println("a=" + a);
    ACCESS_KEY_ID = bundle.getString("accessKeyId").trim();
    int b = ACCESS_KEY_ID.length();
    System.err.println("b=" + b);
    ACCESS_KEY_SECRET = bundle.getString("accessKeySecret").trim();
    int c = ACCESS_KEY_SECRET.length();
    System.err.println("c=" + c);
}

/**
 * 獲取阿里雲OSS客戶端物件
 * */
public static OSSClient getOSSClient() {
    OSSClient ocClient = new OSSClient(ENDPOINT, ACCESS_KEY_ID,
            ACCESS_KEY_SECRET);
    return ocClient;
}

/**
 * 上傳圖片至OSS
 * 
 * @param ossClient
 *            oss連線
 * @param file
 *            上傳檔案(檔案全路徑如:D:\\image\\cake.jpg)
 * @param bucketName
 *            儲存空間
 * @param folder
 *            模擬資料夾名 如"qj_nanjing/"
 * @return String 返回的唯一MD5數字簽名
 * */
public static String uploadObject2OSS(OSSClient ossClient,
        MultipartFile file, String bucketName, String folder,
        String fileName) {
    String resultStr = null;
    try {
        // 以輸入流的形式上傳檔案
        // InputStream is = new FileInputStream(file);
        // 檔名
        // String fileName = file.getOriginalFilename();
        // 檔案大小
        // Long fileSize = file.length();
        // 建立上傳Object的Metadata
        ObjectMetadata metadata = new ObjectMetadata();
        // 上傳的檔案的長度
        metadata.setContentLength(file.getSize());
        // 指定該Object被下載時的網頁的快取行為
        metadata.setCacheControl("no-cache");
        // 指定該Object下設定Header
        metadata.setHeader("Pragma", "no-cache");
        // 指定該Object被下載時的內容編碼格式
        metadata.setContentEncoding("utf-8");
        // 檔案的MIME,定義檔案的型別及網頁編碼,決定瀏覽器將以什麼形式、什麼編碼讀取檔案。如果使用者沒有指定則根據Key或檔名的副檔名生成,
        // 如果沒有副檔名則填預設值application/octet-stream
        // metadata.setContentType(getContentType(fileName));
        // 指定該Object被下載時的名稱(指示MINME使用者代理如何顯示附加的檔案,開啟或下載,及檔名稱)
        // metadata.setContentDisposition("filename/filesize=" + fileName
        // + "/" + fileSize + "Byte.");
        // 上傳檔案 (上傳檔案流的形式)
        PutObjectResult putResult = ossClient.putObject(bucketName, folder
                + fileName, file.getInputStream(), metadata);
        // 解析結果
        resultStr = putResult.getETag();
    } catch (Exception e) {
        e.printStackTrace();

    }

    return resultStr;
}

/**
 * 通過檔名判斷並獲取OSS服務檔案上傳時檔案的contentType
 * 
 * @param fileName
 *            檔名
 * @return 檔案的contentType
 */
public static String getContentType(String fileName) {
    // 檔案的字尾名
    String fileExtension = fileName.substring(fileName.lastIndexOf("."));
    if (".bmp".equalsIgnoreCase(fileExtension)) {
        return "image/bmp";
    }
    if (".gif".equalsIgnoreCase(fileExtension)) {
        return "image/gif";
    }
    if (".jpeg".equalsIgnoreCase(fileExtension)
            || ".jpg".equalsIgnoreCase(fileExtension)
            || ".png".equalsIgnoreCase(fileExtension)) {
        return "image/jpeg";
    }
    if (".html".equalsIgnoreCase(fileExtension)) {
        return "text/html";
    }
    if (".txt".equalsIgnoreCase(fileExtension)) {
        return "text/plain";
    }
    if (".vsd".equalsIgnoreCase(fileExtension)) {
        return "application/vnd.visio";
    }
    if (".ppt".equalsIgnoreCase(fileExtension)
            || "pptx".equalsIgnoreCase(fileExtension)) {
        return "application/vnd.ms-powerpoint";
    }
    if (".doc".equalsIgnoreCase(fileExtension)
            || "docx".equalsIgnoreCase(fileExtension)) {
        return "application/msword";
    }
    if (".xml".equalsIgnoreCase(fileExtension)) {
        return "text/xml";
    }
    // 預設返回型別
    return "image/jpeg";
}

/**
 * 獲得url連結
 * 
 * @param key
 * @return
 */
public static String getUrl(String key) {
    // 設定URL過期時間為10年 3600l* 1000*24*365*10
    Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24
            * 365 * 10);
    // 生成URL
    // 初始化OSSClient
    OSSClient ossClient = OssUtil.getOSSClient();
    URL url = ossClient.generatePresignedUrl("fsg-video", key, expiration);
    if (url != null) {
        return url.toString();
    }
    return null;

}

/**
 * 斷點續傳
 * 
 * @param key
 * @return
 */
public static String multipartUploadObject(String bucketName, String key,
        MultipartFile    partFile) {
    String tag = null;
    String uploadid = null;
    int j = 0;
    // 初始化一個OSSClient
    OSSClient client = getOSSClient();
    ListMultipartUploadsRequest lmur = new ListMultipartUploadsRequest(
            bucketName);

    // 獲取Bucket內所有上傳事件
    MultipartUploadListing listing = client.listMultipartUploads(lmur);
    // 新建一個List儲存每個分塊上傳後的ETag和PartNumber
    List<PartETag> partETags = new ArrayList<PartETag>();

    // 遍歷所有上傳事件 設定UploadId
    for (MultipartUpload multipartUpload : listing.getMultipartUploads()) {
        if (multipartUpload.getKey().equals(key)) {
            uploadid = multipartUpload.getUploadId();
            break;
        }
    }

    if (StringUtils.isEmpty(uploadid)) {
        // 開始Multipart Upload,InitiateMultipartUploadRequest
        // 來指定上傳Object的名字和所屬Bucke
        InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(
                bucketName, key);
        InitiateMultipartUploadResult initiateMultipartUploadResult = client
                .initiateMultipartUpload(initiateMultipartUploadRequest);
        uploadid = initiateMultipartUploadResult.getUploadId();
    } else {
        ListPartsRequest listPartsRequest = new ListPartsRequest(
                bucketName, key, uploadid);
        // listParts 方法獲取某個上傳事件所有已上傳的塊
        PartListing partListing = client.listParts(listPartsRequest);
        // 遍歷所有Part
        for (PartSummary part : partListing.getParts()) {
            partETags
                    .add(new PartETag(part.getPartNumber(), part.getETag()));
            j++;
        }
    }
    // 設定每塊為 5M(最小支援5M)
    final int partSize = 1024 * 1024 * 10;

    try {

        // 計算分塊數目
        int partCount = (int) (partFile.getSize() / partSize);
        if (partFile.getSize() % partSize != 0) {
            partCount++;
        }

        for (int i = j; i < partCount; i++) {
            // 獲取檔案流
             FileInputStream fis;
             fis = (FileInputStream) partFile.getInputStream();

            // 跳到每個分塊的開頭
            long skipBytes = partSize * i;
            fis.skip(skipBytes);

            // 計算每個分塊的大小
            long size = partSize < partFile.getSize() - skipBytes ? partSize
                    : partFile.getSize() - skipBytes;

            // 建立UploadPartRequest,上傳分塊
            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(bucketName);
            uploadPartRequest.setKey(key);
            uploadPartRequest.setUploadId(uploadid);
            uploadPartRequest.setInputStream(fis);
            uploadPartRequest.setPartSize(size);
            uploadPartRequest.setPartNumber(i + 1);

            UploadPartResult uploadPartResult = client
                    .uploadPart(uploadPartRequest);


            // 將返回的PartETag儲存到List中。
            partETags.add(uploadPartResult.getPartETag());

            // 關閉檔案
            fis.close();

        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
            bucketName, key, uploadid, partETags);


    // 完成分塊上傳
    CompleteMultipartUploadResult completeMultipartUploadResult = client
            .completeMultipartUpload(completeMultipartUploadRequest);

    // 列印Object的ETag(返回的ETag不是md5.具體是什麼不詳)
    tag = completeMultipartUploadResult.getETag();
    return tag;
}

}

上傳完成了在說一下我在專案中遇見的問題吧,應為這個是分片上傳的所以要等分片上傳完了才能把分片合成一個完整的視訊,這樣坑就來了,本來上傳一個500多M的視訊就要不少時間在加上傳完後還要合併碎片也要不少時間,這樣就會導致這個介面超時,自己在本地測試的時候並不會報超時,但部署到線上就會出現這個問題,後來發現是負載均衡的問題,因為是用的阿里雲的一站式解決方案,後來也沒有什麼好的解決方案,我們只好不經過負載均衡了直接訪問的,這樣才解決了這一問題。

好了下面給出轉碼的程式碼

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.mts.model.v20140618.SubmitJobsRequest;
import com.aliyuncs.mts.model.v20140618.SubmitJobsResponse;
import com.aliyuncs.profile.DefaultProfile;

public class OSSMTSUtil {
private static final String MTS_REGION = “cn-beijing”;
private static final String OSS_REGION = “oss-cn-beijing”;

private static final String mtsEndpoint = "mts.cn-beijing.aliyuncs.com";

private static String waterMarkFilePath = "";// 水印圖片的key

private static String accessKeyId = "";
private static String accessKeySecret = "";
private static String pipelineId = "";// 轉碼管道ID
private static String transcodeTemplateId = "S00000001-200030";// 預製模板MP4高清
// private static String waterMarkTemplateId = "";//自定義水印模板ID


private static String Bucket = ""; // 儲存空間名

private static DefaultAcsClient aliyunClient;
private static final Logger logger = LoggerFactory
.getLogger(OSSMTSUtil.class);
static {
    //新增多地區
    try {
        DefaultProfile.addEndpoint(MTS_REGION, MTS_REGION, "Mts", mtsEndpoint);
    } catch (ClientException e) {
        logger.error("多地區新增失敗!!");
        e.printStackTrace();
    }
    //獲取轉碼連線物件
    aliyunClient = new DefaultAcsClient(DefaultProfile.getProfile(
            MTS_REGION, accessKeyId, accessKeySecret));


}
/*
 * 
 * 
 * 
 */

  //提交轉碼作業
public static String submitTranscodeJob(OSSFileDO inputFile,
        OSSFileDO watermarkFile,String MTSFlieName) {
    /*JSONObject waterMarkConfig = new JSONObject();
    waterMarkConfig.put("InputFile", watermarkFile.toJson());
    waterMarkConfig.put("WaterMarkTemplateId", waterMarkTemplateId);
    JSONArray waterMarkConfigArray = new JSONArray();
    waterMarkConfigArray.add(waterMarkConfig);*/

    JSONObject jobConfig = new JSONObject();
    jobConfig.put("OutputObject", MTSFlieName);
    jobConfig.put("TemplateId", transcodeTemplateId);
//  jobConfig.put("WaterMarks", waterMarkConfigArray);
    JSONArray jobConfigArray = new JSONArray();
    jobConfigArray.add(jobConfig);

    SubmitJobsRequest request = new SubmitJobsRequest();

    request.setInput(inputFile.toJson().toJSONString());
    request.setOutputBucket(Bucket);
    request.setOutputs(jobConfigArray.toJSONString());
    request.setPipelineId(pipelineId);
    request.setOutputLocation(OSS_REGION);

    Integer outputJobCount = 1;

    SubmitJobsResponse response = null;
    try {
        //傳送轉碼請求
        response = aliyunClient.getAcsResponse(request);
        if (response.getJobResultList().size() != outputJobCount) {
            throw new RuntimeException("SubmitJobsRequest Size failed");
        }

        return response.getJobResultList().get(0).getJob().getJobId();
    } catch (ServerException e) {
        logger.error("轉碼請求提交失敗!!");
        throw new RuntimeException("submitTranscodeJob Server failed");
    } catch (ClientException e) {
        logger.error("轉碼請求提交失敗!!");
        throw new RuntimeException("submitTranscodeJob Client failed");
    }
}

}

import com.alibaba.fastjson.JSONObject;

public class OSSFileDO {
public void setBucket(String bucket) {
this.bucket = bucket;
}

public void setObject(String object) {
    this.object = object;
}

public void setLocation(String location) {
    this.location = location;
}

public String toJsonString(){
    return toJson().toJSONString();
}

public JSONObject toJson(){
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("Bucket", bucket);
    jsonObject.put("Location", location);
    jsonObject.put("Object", object);

    return jsonObject;
}

public OSSFileDO(){

}

public OSSFileDO(String location, String bucket, String object){
    this.location = location;
    this.bucket = bucket;
    this.object = object;
}

private String location;
private String bucket;
private String object;

}

上面註釋掉的是加上水印的,各位根據自己的實際情況選擇,還有在轉碼的時候有個坑,就是在提交轉碼時如果你上次的視訊有中文名,轉碼那邊是識別不了的,按阿里的要求轉碼了就會報介面MD5檢測失敗,還沒有找到什麼好的解決辦法

  • 引用 Ctrl + Q
  • 插入連結 Ctrl + L
  • 插入程式碼 Ctrl + K
  • 插入圖片 `Ctrl +
  • 提升標題 Ctrl + H
  • 有序列表 Ctrl + O
  • 無序列表 Ctrl + U
  • 橫線 Ctrl + R
  • 撤銷 Ctrl + Z
  • 重做 Ctrl + Y

Markdown及擴充套件

Markdown 是一種輕量級標記語言,它允許人們使用易讀易寫的純文字格式編寫文件,然後轉換成格式豐富的HTML頁面。 —— [ 維基百科 ]

使用簡單的符號標識不同的標題,將某些文字標記為粗體或者斜體,建立一個連結等,詳細語法參考幫助?。

本編輯器支援 Markdown Extra ,  擴充套件了很多好用的功能。具體請參考Github.

表格

Markdown Extra 表格語法:

專案 價格
Computer $1600
Phone $12
Pipe $1

可以使用冒號來定義對齊方式:

專案 價格 數量
Computer 1600 元 5
Phone 12 元 12
Pipe 1 元 234

定義列表

Markdown Extra 定義列表語法:
專案1
專案2
定義 A
定義 B
專案3
定義 C

定義 D

定義D內容

程式碼塊

程式碼塊語法遵循標準markdown程式碼,例如:

@requires_authorization
def somefunc(param1='', param2=0):
    '''A docstring'''
    if param1 > param2: # interesting
        print 'Greater'
    return (param2 - param1 + 1) or None
class SomeClass:
    pass
>>> message = '''interpreter
... prompt'''

腳註

生成一個腳註1.

目錄

[TOC]來生成目錄:

數學公式

  • 行內公式,數學公式為:Γ(n)=(n1)!nN
  • 塊級公式:
x=b±b24ac2a

更多LaTex語法請參考 這兒.

UML 圖:

可以渲染序列圖:

Created with Raphaël 2.1.0張三張三李四李四嘿,小四兒, 寫部落格了沒?李四愣了一下,說:忙得吐血,哪有時間寫。

或者流程圖:

Created with Raphaël 2.1.0開始我的操作確認?結束yesno
  • 關於 序列圖 語法,參考 這兒,
  • 關於 流程圖 語法,參考 這兒.

離線寫部落格

即使使用者在沒有網路的情況下,也可以通過本編輯器離線寫部落格(直接在曾經使用過的瀏覽器中輸入write.blog.csdn.net/mdeditor即可。Markdown編輯器使用瀏覽器離線儲存將內容儲存在本地。

使用者寫部落格的過程中,內容實時儲存在瀏覽器快取中,在使用者關閉瀏覽器或者其它異常情況下,內容不會丟失。使用者再次開啟瀏覽器時,會顯示上次使用者正在編輯的沒有發表的內容。

部落格發表後,本地快取將被刪除。 

使用者可以選擇 把正在寫的部落格儲存到伺服器草稿箱,即使換瀏覽器或者清除快取,內容也不會丟失。

注意:雖然瀏覽器儲存大部分時候都比較可靠,但為了您的資料安全,在聯網後,請務必及時發表或者儲存到伺服器草稿箱

瀏覽器相容

  1. 目前,本編輯器對Chrome瀏覽器支援最為完整。建議大家使用較新版本的Chrome。
  2. IE9以下不支援
  3. IE9,10,11存在以下問題
    1. 不支援離線功能
    2. IE9不支援檔案匯入匯出
    3. IE10不支援拖拽檔案匯入
  1. 這裡是 腳註內容.