go 安全通道(缓存器)缓存池 io.Reader error类

本文探讨了在并发编程中如何使用缓冲器来避免运行时恐慌,重点介绍了非阻塞Put和Get方法的设计,以及Close方法确保缓冲器状态一致性的重要性。
  •  向一个已经关闭的通道发送值和关闭一个已经关闭的通道,

都会引发运行时候的恐慌,缓冲器就是解决这个问题诞生的。
Put 方法先检查缓冲器实例是否关闭,并且保证只有在检查结果是否的时候
进行存入。,在Close 方法中仅在当前缓冲器实例未关闭的情况下
进行关闭操作

package buffer

import (
	"sync"
	"sync/atomic"
)

import (
	"fmt"
	"gopcp.v2/chapter6/webcrawler/toolkit/buffer"
	"sync"
)

/***
向一个已经关闭的通道发送值和关闭一个已经关闭的通道,
都会引发运行时候的恐慌,缓冲器就是解决这个问题诞生的。
Put 方法先检查缓冲器实例是否关闭,并且保证只有在检查结果是否的时候
进行存入。,在Close 方法中仅在当前缓冲器实例未关闭的情况下
进行关闭操作
 */
// Buffer 代表FIFO的缓冲器的接口类型。
type Buffer interface {
	// Cap 用于获取本缓冲器的容量。
	Cap() uint32
	// Len 用于获取本缓冲器中的数据数量。
	Len() uint32
	// Put 用于向缓冲器放入数据。
	// 注意!本方法应该是非阻塞的。
	// 若缓冲器已关闭则会直接返回非nil的错误值。
	Put(datum interface{}) (bool, error)
	// Get 用于从缓冲器获取器。
	// 注意!本方法应该是非阻塞的。
	// 若缓冲器已关闭则会直接返回非nil的错误值。
	Get() (interface{}, error)
	// Close 用于关闭缓冲器。
	// 若缓冲器之前已关闭则返回false,否则返回true。
	Close() bool
	// Closed 用于判断缓冲器是否已关闭。
	Closed() bool
}

type myBuffer struct {
	//ch 代表采访数据的通道
	ch chan interface{}
	//closed 代表缓冲器的关闭状态
	closed uint32
	//closingLock 代表为了消除因关闭缓冲器而产生的竟态条件的读写锁
	closingLock sync.RWMutex
}

func NewBuffer(size uint32)(Buffer, error){
	if size==0 {
		errMsg := fmt.Sprintf(" illegal size for buffer:%d",size)
		return nil, error.NewIllegalParameterError(errMsg)
	}

	return &myBuffer{
		ch : make(chan interface{},size),
	},nil
}

func (buf *myBuffer) Cap() uint32 {
	return uint32(cap(buf.ch))
}

func (buf *myBuffer) Len() uint32 {
	return uint32(len(buf.ch))
}

func (buf *myBuffer) Put (datum interface{}) (ok bool,err error){
		buf.closingLock.RLock()
		defer buf.closingLock.Unlock()

		if buf.Closed() {
			return false,ErrClosedBuffer
		}

		select {
		case buf.ch <- datum:
			ok = true
		default:
			ok = false
		}

		return
}

func (buf *myBuffer) Get() (interface{}, error) {
	select {
	case datum, ok := <-buf.ch:
		if !ok {
			return nil, ErrClosedBuffer
		}
		return datum, nil
	default:
		return nil, nil
	}
}

func (buf *myBuffer)Close() bool {
	if atomic.CompareAndSwapUint32(&buf.closed,0,1){
		buf.closingLock.Lock()
		close(buf.ch)
		buf.closingLock.Unlock()
		return true
	}

	return false
}
  • io.Reader 

io.Reader 实现类型:bytes.Reader  bufio.Reader  ,这类读取器只能够读取他们底层的一遍数据,当读完数据后,他们的Read 方法总会把io.EOF 变量的值做为错误值返回。net/http 中的 http.Reaponse 类型的body 就是 io.Readcloser接口类型的,io.Readcloser的类型声明嵌入了io.Reader,前者只比后者多一个Close的方法。   

  • 错误类
package errors

import (
	"bytes"
	"fmt"
	"strings"
)

type ErrorType string

const (
	// ERROR_TYPE_DOWNLOADER 代表下载器错误。
	ERROR_TYPE_DOWNLOADER ErrorType = "downloader error"
	// ERROR_TYPE_ANALYZER 代表分析器错误。
	ERROR_TYPE_ANALYZER ErrorType = "analyzer error"
	// ERROR_TYPE_PIPELINE 代表条目处理管道错误。
	ERROR_TYPE_PIPELINE ErrorType = "pipeline error"
	// ERROR_TYPE_SCHEDULER 代表调度器错误。
	ERROR_TYPE_SCHEDULER ErrorType = "scheduler error"
)

type CrawlerError interface {
	Type() ErrorType
	error() string
}

type myCrawlerError struct{
	errType ErrorType
	errMsg  string
	fullErrMsg string
}

// NewCrawlerError 用于创建一个新的爬虫错误值。
func NewCrawlerError(errType ErrorType, errMsg string) CrawlerError {
	return &myCrawlerError{
		errType: errType,
		errMsg:  strings.TrimSpace(errMsg),
	}
}

// NewCrawlerErrorBy 用于根据给定的错误值创建一个新的爬虫错误值。
func NewCrawlerErrorBy(errType ErrorType, err error) CrawlerError {
	return NewCrawlerError(errType, err.Error())
}

func (ce *myCrawlerError) Type() ErrorType {
	return ce.errType
}

func (ce *myCrawlerError) Error() string {
	if ce.fullErrMsg == "" {
		ce.genFullErrMsg()
	}
	return ce.fullErrMsg
}

func (ce *myCrawlerError) getFullErrMsg() {
	var buffer bytes.Buffer

	buffer.WriteString("crawler error:")
	if ce.errType!="" {
		buffer.WriteString(string(ce.errType))
		buffer.WriteString(":")
	}

	buffer.WriteString(ce.errMsg)
	ce.fullErrMsg =fmt.Sprintf("%s",buffer.String())
	return
}

// IllegalParameterError 代表非法的参数的错误类型。
type IllegalParameterError struct {
	msg string
}

// NewIllegalParameterError 会创建一个IllegalParameterError类型的实例。
func NewIllegalParameterError(errMsg string) IllegalParameterError {
	return IllegalParameterError{
		msg: fmt.Sprintf("illegal parameter: %s",
			strings.TrimSpace(errMsg)),
	}
}

func (ipe IllegalParameterError) Error() string {
	return ipe.msg
}

 

 

<think>我们使用H5+App(HTML5Plus App)开发,可以调用5+ Runtime的API。这里我们需要实现两个功能:1.将页面的JSON数据缓存至手机本地的文件中。2.从文件中读取数据并显示。我们将使用以下5+API:-plus.io.PRIVATE_DOC:应用私有文档目录,存储应用私有文件,通常用于保存应用运行时的数据。 -plus.io.resolveLocalFileSystemURL:用于获取本地文件系统对象。- plus.io.FileWriter:用于写入文件。- plus.io.FileReader:用于读取文件。步骤:1.缓存JSON数据到文件:a.将JSON数据转换为字符串。b.获取目标目录(PRIVATE_DOC)下的文件对象。c.创建文件(如果不存在)并写入数据。2.从文件中读取数据:a.获取目标目录下的文件对象。b.读取文件内容,并解析为JSON对象(或直接显示字符串)。注意:由于5+ API是异步的,我们需要使用Promise或者回调函数来处理。下面是一个示例,假设我们有一个按钮用于保存数据,另一个按钮用于读取数据。我们将创建一个名为`data.json`的文件保存在私有文档目录下。代码实现如下(使用JavaScript):</think>下面我将详细讲解如何在H5+ App中使用5+ API实现JSON数据的本地存储与读取功能。核心思路是利用`plus.io`文件系统API操作设备本地存储空间。 ### 完整解决方案代码 ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JSON数据存储示例</title> <script> // 要存储的JSON数据示例 const sampleData = { appVersion: "1.0.0", lastLogin: "2023-06-15T08:30:00Z", userSettings: { theme: "dark", notifications: true } }; // 保存JSON到文件 function saveData() { // 获取私有文档目录路径 const dirPath = plus.io.PRIVATE_DOC; plus.io.resolveLocalFileSystemURL(dirPath, function(entry) { // 创建或获取文件 entry.getFile("app_data.json", { create: true }, function(fileEntry) { fileEntry.createWriter(function(writer) { writer.onwriteend = function(e) { console.log("保存成功!"); alert(`数据已保存至: ${fileEntry.toURL()}`); }; writer.onerror = function(e) { console.error("保存失败: " + e.toString()); }; // 将JSON转为字符串并写入 const jsonStr = JSON.stringify(sampleData); writer.write(jsonStr); }, errorHandler); }, errorHandler); }, errorHandler); } // 从文件读取JSON function loadData() { const dirPath = plus.io.PRIVATE_DOC; plus.io.resolveLocalFileSystemURL(dirPath, function(entry) { entry.getFile("app_data.json", {}, function(fileEntry) { fileEntry.file(function(file) { const reader = new FileReader(); reader.onloadend = function(e) { try { const jsonData = JSON.parse(e.target.result); displayData(jsonData); } catch (e) { console.error("JSON解析失败: " + e); } }; reader.readAsText(file); }, errorHandler); }, errorHandler); }, errorHandler); } // 显示数据到页面 function displayData(data) { const displayArea = document.getElementById('dataDisplay'); displayArea.innerHTML = ` <h3>读取到的数据:</h3> <pre>${JSON.stringify(data, null, 2)}</pre> <p>存储路径: ${plus.io.PRIVATE_DOC}/app_data.json</p> `; } // 错误处理 function errorHandler(e) { console.error("操作失败: " + e.message); alert("操作失败: " + e.message); } </script> </head> <body> <h2>JSON数据存储示例</h2> <button onclick="saveData()">保存JSON数据</button> <button onclick="loadData()">读取JSON数据</button> <div id="dataDisplay"></div> </body> </html> ``` ### 代码关键点解析 1. **存储目录选择**: ```javascript const dirPath = plus.io.PRIVATE_DOC; // 应用私有文档目录 ``` - `PRIVATE_DOC`:应用私有目录(推荐),路径如:`_doc/` - `PRIVATE_WWW`:应用资源目录(只读) - `PUBLIC_DOCUMENTS`:公共文档目录(需权限) 2. **文件写入流程**: ```javascript plus.io.resolveLocalFileSystemURL(dirPath, function(entry) { entry.getFile("app_data.json", { create: true }, function(fileEntry) { fileEntry.createWriter(function(writer) { writer.write(JSON.stringify(sampleData)); }); }); }); ``` - `resolveLocalFileSystemURL`:获取目录对象 - `getFile`:创建/获取文件(`create: true`自动创建) - `createWriter`:创建文件写入器 3. **文件读取流程**: ```javascript entry.getFile("app_data.json", {}, function(fileEntry) { fileEntry.file(function(file) { const reader = new FileReader(); reader.readAsText(file); reader.onloadend = function(e) { JSON.parse(e.target.result); }; }); }); ``` - 使用标准`FileReader`API读取文本 - `JSON.parse`解析字符串为JSON对象 4. **错误处理**: ```javascript function errorHandler(e) { console.error("操作失败: " + e.message); } ``` 统一处理文件操作中的错误(权限不足/存储空间满等) ### 存储路径说明 | 目录型 | 路径示例 | 特点 | |----------------------|-----------------------------|-------------------------------| | `PRIVATE_DOC` | `_doc/app_data.json` | 应用私有,无需权限,推荐使用 | | `PUBLIC_DOCUMENTS` | `Documents/app_data.json` | 系统公共目录,需要权限 | | `PRIVATE_WWW` | `_www/app_data.json` | 只读,不可写入用户数据 | ### 实际应用建议 1. **敏感数据存储**:对敏感数据加密后再存储 2. **大数据处理**:使用`plus.io.requestFileSystem`分块写入 3. **版本兼容**:添加数据版本字段便于后续升级 4. **错误恢复**:实现读取失败时的默认数据加载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值