Swift 云存儲(chǔ)(阿里云环葵、騰訊云) 上傳工具看我就夠了

前言

iOS 開發(fā)中需要用到第三方文件儲(chǔ)存调窍,那么在國(guó)內(nèi)一般就是使用這兩個(gè)比較多,國(guó)外的話一般就是亞馬遜张遭,不過都是大同小異邓萨,在這邊我代碼介紹下阿里騰訊SDK的上傳工具如何使用,上代碼

一菊卷、 阿里云

1.pod 'AliyunOSSiOS' pod導(dǎo)入sdk

  1. UploadParamModel 為配置model缔恳,所有配置信息由后臺(tái)下發(fā),我這邊圖片用的是webp格式僅支持iOS14 + 如果你用的普通png洁闰,jpeg歉甚,那么直接拿UIImage 的data即可。
    3.創(chuàng)建一個(gè)AliYunUtil 文件,我這邊有用到 ffmpegkit 視頻壓縮(如果不需要就直接拿視頻路徑上傳即可)扑眉、以及PromiseKit 異步編程工具(如果不需要?jiǎng)t自己修改邏輯)纸泄,大部分應(yīng)該是可以看懂的:

import Foundation
import UIKit
import PromiseKit
import libwebp
import AVFoundation
import ffmpegkit
import AliyunOSSiOS

class AliYunUtil {
    
    static let share = AliYunUtil()
    
    private var client: OSSClient!
    
//配置model
    var configModel: UploadParamModel!
    
    func setupOSSClient() {
        // 初始化具有自動(dòng)刷新的provider
        let credentialProvider = OSSStsTokenCredentialProvider(accessKeyId: self.configModel.accessKeyId,
                                                               secretKeyId: self.configModel.accessKeySecret,
                                                               securityToken: self.configModel.securityToken)
        
        self.client = OSSClient(endpoint: self.configModel.endpoint, credentialProvider: credentialProvider)
    }
    
    
    // MARK: - Public
    
    /// 上傳圖片
    public func uploadImage(images: [UIImage], contentType: AmazonContentType, prefixType: AmazonURLType, fileType: AmazonFileType) -> Promise<[String]> {
        // 返回Promise
        return Promise<[String]> { resolver in
            // 請(qǐng)求上傳參數(shù)
            CommonRequest.uploadParams(number: images.count, prefixType: prefixType, type: fileType).done { result in
                // 獲取后臺(tái)返回的參數(shù)
                guard let uploadParams = result.model else {
                    resolver.reject(RequestError(code: result.code, data: result.data, message: result.msg))
                    return
                }
                self.configModel = uploadParams
                self.setupOSSClient()
                // 準(zhǔn)備上傳
                var uploads: [Promise<String>] = []
                DispatchQueue.global().async {
                    for (i, image) in images.enumerated() {
                        let data_count = image.jpegData(compressionQuality: 1)?.count ?? 0
                        print("原始圖片大小:\(data_count)")
                        let corver_data = UIImage.image(toWebP: image, compressionQuality: 100)
                        print("webp轉(zhuǎn)換成功,大醒亍:\(corver_data.count)");
                        if i < uploadParams.nameList.count {
                            let name = uploadParams.nameList[i]
                            let uploadRequest = self.uploadData(corver_data, contentType: contentType, uploadParams: uploadParams, name: name)
                             uploads.append(uploadRequest)
                        }
                    }
                    
                    // 上傳 
                    when(fulfilled: uploads).done { names in
                        resolver.fulfill(names)
                    }.catch { error in
                        resolver.reject(error)
                    }
                    
                }
            }.catch { error in
                resolver.reject(error)
            }
        }
    }
  
    /// 上傳視頻
    public  func uploadVideo(fileURLs: [URL], contentType: AmazonContentType, prefixType: AmazonURLType, fileType: AmazonFileType) -> Promise<[String]> {
        // 返回Promise
        return Promise<[String]> { resolver in
            // 請(qǐng)求上傳參數(shù)
            CommonRequest.uploadParams(number: fileURLs.count, prefixType: .chat, type: fileType).done { result in
                // 獲取后臺(tái)返回的參數(shù)
                guard let uploadParams = result.model else {
                    resolver.reject(RequestError(code: result.code, data: result.data, message: result.msg))
                    return
                }
                self.configModel = uploadParams
                self.setupOSSClient()
                // 準(zhǔn)備上傳
                var uploads: [Promise<String>] = []
                for (i, fileURL) in fileURLs.enumerated() {
                    if i < uploadParams.nameList.count {
                        let name = uploadParams.nameList[i]
                        let uploadRequest = self.uploadVideo(fileURL, contentType: contentType, uploadParams: uploadParams, name: name)
                        uploads.append(uploadRequest)
                    }
                }
                // 上傳
                when(fulfilled: uploads).done { names in
                    resolver.fulfill(names)
                }.catch { error in
                    resolver.reject(error)
                }
            }.catch { error in
                resolver.reject(error)
            }
        }
    }
    
    /// 上傳單個(gè)視頻和對(duì)應(yīng)的封面
    public  func uploadVideoAndCover(coverImage: UIImage, videoURL:URL, contentType: AmazonContentType) -> Promise<[String]> {
        return Promise<[String]> { resolver in
            var uploads: [Promise<[String]>] = []
            let image_request = self.uploadImage(images: [coverImage], contentType: .webp, prefixType: .chat, fileType: .image)
            uploads.append(image_request)
            let video_request = self.uploadVideo(fileURLs: [videoURL], contentType: contentType, prefixType: .chat, fileType: .video)
            uploads.append(video_request)
            // 上傳
            when(fulfilled: uploads).done { names in
                guard let imageURLs = names.first, let videoURLs = names.last else {
                    return
                }
                guard let videoURL = videoURLs.first, let imageURL = imageURLs.first else {
                    return
                }
                resolver.fulfill([imageURL, videoURL])
            }.catch { error in
                resolver.reject(error)
            }
        }
    }
 
    // MARK: - Private
    /// 上傳圖片文件
    private  func uploadData(_ data: Data, contentType: AmazonContentType, uploadParams: UploadParamModel, name: String) -> Promise<String> {
        // 返回Promise
        return Promise<String> { resolver in
            // 設(shè)置參數(shù)
            let fileName  = name + contentType.suffix
            let put = OSSPutObjectRequest()
            put.bucketName = uploadParams.bucketName
            put.objectKey = fileName
            put.contentType = "application/octet-stream"
            put.uploadingData = data
            put.expires = uploadParams.expiration
            put.uploadProgress = { (bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) -> Void in
                print("bytesSent:\(bytesSent),totalBytesSent:\(totalBytesSent),totalBytesExpectedToSend:\(totalBytesExpectedToSend)");
            }
            let putTask = self.client.putObject(put)
            putTask.continue({(task) -> OSSTask<AnyObject>? in
                DispatchAfter(after: 1, handler: {
                    if (task.error != nil) {
                        resolver.reject(RequestError(code: "", data: "", message: "上傳錯(cuò)誤".localized))
                    } else {
                        let key = fileName.components(separatedBy: "/").last ?? fileName
                        resolver.fulfill(key)
                    }
                })
                return nil
            })
        }
    }
    
    /// 上傳視頻文件
    private  func uploadVideo(_ fileURL: URL, contentType: AmazonContentType, uploadParams: UploadParamModel, name: String) -> Promise<String> {
        // 返回Promise
        return Promise<String> { resolver in
            //開始?jí)嚎s
            let inputPath = fileURL
            let outFilePath = WMCameraFileTools.wm_createFileUrl("mp4")
            let avAsset = AVURLAsset(url: fileURL)
            let array = avAsset.tracks
            var size = CGSize.zero
            var bitNumber : Float = 2.0
            for track in array{
                if track.mediaType == .video{
                    size = track.naturalSize
                    let bit  =  track.estimatedDataRate
                    bitNumber = Float((bit / 1000.0 / 1000.0))
                }
            }
            bitNumber = bitNumber < 2.0 ? bitNumber : 2.0
            let z = String(format: "%.2f", bitNumber)
            var cmd = "-i \(inputPath) -b:v \(z)M -vf scale=720:-2 -vcodec libx264 -y \(outFilePath)"
            if (size.width < size.height) {
                cmd = "-i \(inputPath) -b:v \(z)M -vf scale=-2:720 -vcodec libx264 -y \(outFilePath)"
            }
            
            FFmpegKit.executeAsync(cmd) { result in
                DispatchQueue.main.async {
                    // 設(shè)置參數(shù)
                    let fileName = name + ".mp4"
                    let outFileVideoUrl = URL(fileURLWithPath: outFilePath)
                    let put = OSSPutObjectRequest()
                    put.bucketName = uploadParams.bucketName
                    put.objectKey = fileName
                    put.contentType = "application/octet-stream"
                    put.uploadingFileURL = outFileVideoUrl
                    put.expires = uploadParams.expiration
                    put.uploadProgress = { (bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) -> Void in
                        print("bytesSent:\(bytesSent),totalBytesSent:\(totalBytesSent),totalBytesExpectedToSend:\(totalBytesExpectedToSend)");
                    }
                    let putTask = self.client.putObject(put)
                    putTask.continue({(task) -> OSSTask<AnyObject>? in
                        DispatchAfter(after: 1, handler: {
                            if (task.error != nil) {
                                resolver.reject(RequestError(code: "", data: "", message: "上傳錯(cuò)誤".localized))
                            } else {
                                let key = fileName.components(separatedBy: "/").last ?? fileName
                                resolver.fulfill(key)
                            }
                        })
                        return nil
                    })
                }
            }
        }
    }
}

阿里云上傳工具的使用

圖片(photos) 單張或者多張都可以聘裁,所有傳完回調(diào) 拿到

AliyunOSSiOS.share.uploadImage(images: photos, contentType: .webp, prefixType: .chat, fileType: .image).done {[weak self] imagesArray in
              
        //imagesArray 為路徑結(jié)果

        }.catch {[weak self] error in
      
        }

視頻 、帶封面上傳 回調(diào) 拿到視頻路徑(fileURL)弓千、和第一幀(coverImage)圖片路徑

 AliyunOSSiOS.share.uploadVideoAndCover(coverImage: coverImage, videoURL: fileURL, contentType: .video(type: fileURL.pathExtension)).done {[weak self] result in
             
    //result.first!,
 
     //result.last!

        }.catch {[weak self] error in
 
        }

二衡便、 騰訊云

1.pod 'QCloudCOSXML' pod導(dǎo)入sdk

  1. UploadParamModel 為配置model,所有配置信息由后臺(tái)下發(fā)计呈,我這邊圖片用的是webp格式僅支持iOS14 + 如果你用的普通png砰诵,jpeg,那么直接拿UIImage 的data即可捌显。
    3.創(chuàng)建一個(gè)TencentOSS 文件,我這邊有用到 ffmpegkit 視頻壓縮茁彭、以及PromiseKit 異步編程工具,大部分應(yīng)該是可以看懂的:

import Foundation
import PromiseKit
import ffmpegkit
import QCloudCOSXML
import QCloudCore

class TencentOSS : NSObject{
 
    static let share = TencentOSS() 
    var configModel: UploadParamModel!
 
    func setupOSSClient() {
        /// 配置騰訊云桶
        let config = QCloudServiceConfiguration.init()
        config.signatureProvider = self
        config.appID = self.configModel.appId
        let endpoint = QCloudCOSXMLEndPoint.init()
        endpoint.regionName = self.configModel.region
        endpoint.useHTTPS = true
        config.endpoint = endpoint
        QCloudCOSXMLService.registerDefaultCOSXML(with: config)
        QCloudCOSTransferMangerService.registerDefaultCOSTransferManger(with: config) 
    }
    
    
    // MARK: - Public
    
    /// 上傳圖片
    public func uploadImage(images: [UIImage], contentType: AmazonContentType, prefixType: AmazonURLType, fileType: AmazonFileType) -> Promise<[String]> {
        // 返回Promise
        return Promise<[String]> { resolver in
            // 請(qǐng)求上傳參數(shù)
            CommonRequest.uploadParams(number: images.count, prefixType: prefixType, type: fileType).done { result in
                // 獲取后臺(tái)返回的參數(shù)
                guard let uploadParams = result.model else {
                    resolver.reject(RequestError(code: result.code, data: result.data, message: result.msg))
                    return
                }
                self.configModel = uploadParams
                self.setupOSSClient()
                // 準(zhǔn)備上傳
                var uploads: [Promise<String>] = []
                DispatchQueue.global().async {
                    for (i, image) in images.enumerated() {
                        let data_count = image.jpegData(compressionQuality: 1)?.count ?? 0
                        print("原始圖片大蟹鐾帷:\(data_count)")
                        let corver_data = UIImage.image(toWebP: image, compressionQuality: 100) as  NSData
                        print("webp轉(zhuǎn)換成功,大欣矸巍:\(corver_data.count)");
                        if i < uploadParams.nameList.count {
                            let name = uploadParams.nameList[i]
                            let uploadRequest = self.uploadData(corver_data, contentType: contentType, uploadParams: uploadParams, name: name)
                             uploads.append(uploadRequest)
                        }
                    }
                    
                    // 上傳
                    when(fulfilled: uploads).done { names in
                        resolver.fulfill(names)
                    }.catch { error in
                        resolver.reject(error)
                    }
                    
                }
            }.catch { error in
                resolver.reject(error)
            }
        }
    }
  
    /// 上傳視頻
    public  func uploadVideo(fileURLs: [URL], contentType: AmazonContentType, prefixType: AmazonURLType, fileType: AmazonFileType) -> Promise<[String]> {
        // 返回Promise
        return Promise<[String]> { resolver in
            // 請(qǐng)求上傳參數(shù)
            CommonRequest.uploadParams(number: fileURLs.count, prefixType: .chat, type: fileType).done { result in
                // 獲取后臺(tái)返回的參數(shù)
                guard let uploadParams = result.model else {
                    resolver.reject(RequestError(code: result.code, data: result.data, message: result.msg))
                    return
                }
                self.configModel = uploadParams
                self.setupOSSClient()
                // 準(zhǔn)備上傳
                var uploads: [Promise<String>] = []
                for (i, fileURL) in fileURLs.enumerated() {
                    if i < uploadParams.nameList.count {
                        let name = uploadParams.nameList[i]
                        let uploadRequest = self.uploadVideo(fileURL, contentType: contentType, uploadParams: uploadParams, name: name)
                        uploads.append(uploadRequest)
                    }
                }
                // 上傳
                when(fulfilled: uploads).done { names in
                    resolver.fulfill(names)
                }.catch { error in
                    resolver.reject(error)
                }
            }.catch { error in
                resolver.reject(error)
            }
        }
    }
    
    /// 上傳單個(gè)視頻和對(duì)應(yīng)的封面
    public  func uploadVideoAndCover(coverImage: UIImage, videoURL:URL, contentType: AmazonContentType) -> Promise<[String]> {
        return Promise<[String]> { resolver in
            var uploads: [Promise<[String]>] = []
            let image_request = self.uploadImage(images: [coverImage], contentType: .webp, prefixType: .chat, fileType: .image)
            uploads.append(image_request)
            let video_request = self.uploadVideo(fileURLs: [videoURL], contentType: contentType, prefixType: .chat, fileType: .video)
            uploads.append(video_request)
            // 上傳
            when(fulfilled: uploads).done { names in
                guard let imageURLs = names.first, let videoURLs = names.last else {
                    return
                }
                guard let videoURL = videoURLs.first, let imageURL = imageURLs.first else {
                    return
                }
                resolver.fulfill([imageURL, videoURL])
            }.catch { error in
                resolver.reject(error)
            }
        }
    }
 
    // MARK: - Private
    /// 上傳圖片文件
    private  func uploadData(_ data: NSData, contentType: AmazonContentType, uploadParams: UploadParamModel, name: String) -> Promise<String> {
        // 返回Promise
        return Promise<String> { resolver in
            // 設(shè)置參數(shù)
            let fileName  = name + contentType.suffix
            let put : QCloudCOSXMLUploadObjectRequest = QCloudCOSXMLUploadObjectRequest<AnyObject>()
            // 存儲(chǔ)桶名稱,由BucketName-Appid 組成
            put.bucket = uploadParams.bucket
            // 對(duì)象鍵善镰,是對(duì)象在 COS 上的完整路徑妹萨,如果帶目錄的話,格式為 "video/xxx/movie.mp4"
            put.object = fileName
            //需要上傳的對(duì)象內(nèi)容炫欺『跬辏可以傳入NSData*或者NSURL*類型的變量
            put.body = data
            //監(jiān)聽上傳結(jié)果
            put.setFinish { (result, error) in
                // 獲取上傳結(jié)果
                DispatchAfter(after: 0.5, handler: {
                    if (error != nil) {
                        resolver.reject(RequestError(code: "", data: "", message: "上傳錯(cuò)誤".localized))
                    } else {
                        print(result!)
                        let key = fileName.components(separatedBy: "/").last ?? fileName
                        resolver.fulfill(key)
                    }
                })
            }
            //監(jiān)聽上傳進(jìn)度
            put.sendProcessBlock = { (bytesSent, totalBytesSent,
                totalBytesExpectedToSend) in
                //bytesSent                 本次要發(fā)送的字節(jié)數(shù)(一個(gè)大文件可能要分多次發(fā)送)
                //totalBytesSent            已發(fā)送的字節(jié)數(shù)
                //totalBytesExpectedToSend  本次上傳要發(fā)送的總字節(jié)數(shù)(即一個(gè)文件大小)
                print("bytesSent:\(bytesSent),totalBytesSent:\(totalBytesSent),totalBytesExpectedToSend:\(totalBytesExpectedToSend)");
                 
            }
            QCloudCOSTransferMangerService.defaultCOSTransferManager().uploadObject(put)
        }
    }
    
    /// 上傳視頻文件
    private  func uploadVideo(_ fileURL: URL, contentType: AmazonContentType, uploadParams: UploadParamModel, name: String) -> Promise<String> {
        // 返回Promise
        return Promise<String> { resolver in
            //開始?jí)嚎s
            let inputPath = fileURL
            let outFilePath = WMCameraFileTools.wm_createFileUrl("mp4")
            let avAsset = AVURLAsset(url: fileURL)
            let array = avAsset.tracks
            var size = CGSize.zero
            var bitNumber : Float = 2.0
            for track in array{
                if track.mediaType == .video{
                    size = track.naturalSize
                    let bit  =  track.estimatedDataRate
                    bitNumber = Float((bit / 1000.0 / 1000.0))
                }
            }
            bitNumber = bitNumber < 2.0 ? bitNumber : 2.0
            let z = String(format: "%.2f", bitNumber)
            var cmd = "-i \(inputPath) -b:v \(z)M -vf scale=720:-2 -vcodec libx264 -y \(outFilePath)"
            if (size.width < size.height) {
                cmd = "-i \(inputPath) -b:v \(z)M -vf scale=-2:720 -vcodec libx264 -y \(outFilePath)"
            }
            
            FFmpegKit.executeAsync(cmd) { result in
                DispatchQueue.main.async {
                    
                    let fileName = name + ".mp4"
                    let outFileVideoUrl = NSURL(fileURLWithPath: outFilePath)
                    let put : QCloudCOSXMLUploadObjectRequest = QCloudCOSXMLUploadObjectRequest<AnyObject>()
                    put.bucket = uploadParams.bucket
                    put.object = fileName
                    put.body = outFileVideoUrl
                    //監(jiān)聽上傳結(jié)果
                    put.setFinish { (result, error) in
                        // 獲取上傳結(jié)果
                        if (error != nil) {
                            resolver.reject(RequestError(code: "", data: "", message: "上傳錯(cuò)誤".localized))
                        } else {
                            print(result!)
                            let key = fileName.components(separatedBy: "/").last ?? fileName
                            resolver.fulfill(key)
                        } 
                    }
                    //監(jiān)聽上傳進(jìn)度
                    put.sendProcessBlock = { (bytesSent, totalBytesSent,
                        totalBytesExpectedToSend) in
                        //bytesSent                 本次要發(fā)送的字節(jié)數(shù)(一個(gè)大文件可能要分多次發(fā)送)
                        //totalBytesSent            已發(fā)送的字節(jié)數(shù)
                        //totalBytesExpectedToSend  本次上傳要發(fā)送的總字節(jié)數(shù)(即一個(gè)文件大衅仿濉)
                        print("bytesSent:\(bytesSent),totalBytesSent:\(totalBytesSent),totalBytesExpectedToSend:\(totalBytesExpectedToSend)");
                         
                    }
                    QCloudCOSTransferMangerService.defaultCOSTransferManager().uploadObject(put)
                }
            }
        }
    }
}
 
// 配置簽名
extension TencentOSS : QCloudSignatureProvider {
 
    func signature(with fileds: QCloudSignatureFields!, request: QCloudBizHTTPRequest!, urlRequest urlRequst: NSMutableURLRequest!, compelete continueBlock: QCloudHTTPAuthentationContinueBlock!) {
 
        
        PLog("配置后臺(tái)返回的臨時(shí)文件進(jìn)行簽名===\(self.configModel.kj.JSONString())")
        
        let credential = QCloudCredential.init();
              // 臨時(shí)密鑰 SecretId
        credential.secretID = self.configModel.tmpSecretId
              // 臨時(shí)密鑰 SecretKey
              credential.secretKey = self.configModel.tmpSecretKey
              // 臨時(shí)密鑰 Token
        credential.token =  self.configModel.sessionToken
              /** 強(qiáng)烈建議返回服務(wù)器時(shí)間作為簽名的開始時(shí)間, 用來避免由于用戶手機(jī)本地時(shí)間偏差過大導(dǎo)致的簽名不正確(參數(shù)startTime和expiredTime單位為秒)
              */
      
        credential.startDate = Date.init(timeIntervalSince1970: TimeInterval(self.configModel.startTime)!)
        credential.expirationDate = Date.init(timeIntervalSince1970: TimeInterval(self.configModel.expiredTime)!)

      let creator = QCloudAuthentationV5Creator.init(credential: credential);
      // 注意 這里不要對(duì)urlRequst 進(jìn)行copy以及mutableCopy操作
      let signature = creator?.signature(forData: urlRequst);
      continueBlock(signature,nil);
       
    }
    
}
使用方法和阿里云一樣 把 AliYunUtil 換成 TencentOSS 即可 树姨,有不明白的可以私??

?? 騰訊云上面用的是 分片上傳摩桶,如果不需要?jiǎng)t使用簡(jiǎn)單上傳。這樣寫

//視頻
                   let fileName = name + ".mp4"
                    let outFileVideoUrl = NSURL(fileURLWithPath: outFilePath) 
                    let putObject = QCloudPutObjectRequest<AnyObject>.init()
                    putObject.bucket = uploadParams.bucket
                    putObject.body =  outFileVideoUrl
                    putObject.object = fileName
                    putObject.finishBlock = {(result,error) in
                        // 獲取上傳結(jié)果
                        if (error != nil) {
                            resolver.reject(RequestError(code: "", data: "", message: "上傳錯(cuò)誤".localized))
                        } else {
                            print(result!)
                            let key = fileName.components(separatedBy: "/").last ?? fileName
                            resolver.fulfill(key)
                        }
                    }
                    QCloudCOSXMLService.defaultCOSXML().putObject(putObject)

//圖片

 // 設(shè)置參數(shù)
            let fileName  = name + contentType.suffix
            let putObject = QCloudPutObjectRequest<AnyObject>.init()
            putObject.bucket = uploadParams.bucket
            putObject.body =  data
            putObject.object = fileName
            putObject.finishBlock = {(result,error) in
                DispatchAfter(after: 0.5, handler: {
                    if (error != nil) {
                        resolver.reject(RequestError(code: "", data: "", message: "上傳錯(cuò)誤".localized))
                    } else {
                        print(result!)
                        let key = fileName.components(separatedBy: "/").last ?? fileName
                        resolver.fulfill(key)
                    }
                })
            }
            QCloudCOSXMLService.defaultCOSXML().putObject(putObject)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帽揪,一起剝皮案震驚了整個(gè)濱河市硝清,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌转晰,老刑警劉巖芦拿,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異查邢,居然都是意外死亡蔗崎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門扰藕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚁趁,“玉大人,你說我怎么就攤上這事实胸∷眨” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵庐完,是天一觀的道長(zhǎng)钢属。 經(jīng)常有香客問我,道長(zhǎng)门躯,這世上最難降的妖魔是什么淆党? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮讶凉,結(jié)果婚禮上染乌,老公的妹妹穿的比我還像新娘。我一直安慰自己懂讯,他們只是感情好荷憋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著褐望,像睡著了一般勒庄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瘫里,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天实蔽,我揣著相機(jī)與錄音,去河邊找鬼谨读。 笑死局装,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铐尚,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼阶冈,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了塑径?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤填具,失蹤者是張志新(化名)和其女友劉穎统舀,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劳景,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡誉简,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盟广。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闷串。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖筋量,靈堂內(nèi)的尸體忽然破棺而出烹吵,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站尚揣,受9級(jí)特大地震影響客给,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恨课,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窿吩,春花似錦、人聲如沸错览。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倾哺。三九已至先较,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悼粮,已是汗流浹背闲勺。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扣猫,地道東北人菜循。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像申尤,于是被迫代替她去往敵國(guó)和親癌幕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衙耕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容