首页 文章 精选 留言 我的

精选列表

搜索[读写分离],共10005篇文章
优秀的个人博客,低调大师

前后分离模型之封装 Api 调用

Ajax 和异步处理 调用 API 访问数据采用的 Ajax 方式,这是一个异步过程,异步过程最基本的处理方式是事件或回调,其实这两种处理方式实现原理差不多,都需要在调用异步过程的时候传入一个在异步过程结束的时候调用的接口。比如 jQuery Ajax 的 success 就是典型的回调参数。不过使用 jQuery 处理异步推荐使用 Promise 处理方式。 Promise 处理方式也是通过注册回调函数来完成的。jQuery 的 Promise 和 ES6 的标准 Promise 有点不一样,但在 then 上可以兼容,通常称为 thenable。jQuery 的 Promise 没有提供 .catch() 接口,但它自己定义的 .done()、.fail() 和 .always() 三个注册回调的方式也很有特色,用起来很方便,它是在事件的方式来注册的(即,可以注册多个同类型的处理函数,在该触发的时候都会触发)。 当然更直观的一点的处理方式是使用 ES2017 带来的 async/await 方式,可以用同步代码的形式来写异步代码,当然也有一些坑在里面。对于前端工程师来说,最大的坑就是有些浏览器不支持,需要进行转译,所以如果前端代码没有构建过程,一般还是就用 ES5 的语法兼容性好一些(jQuery 的 Promise 是支持 ES5 的,但是标准 Promise 要 ES6 以后才可以使用)。 关于 JavaScript 异步处理相关的内容可以参考 从小小题目逐步走进 JavaScript 异步调用 闲谈异步调用“扁平”化 从地狱到天堂,Node 回调向 async/await 转变 理解 JavaScript 的 async/await 从不用 try-catch 实现的 async/await 语法说错误处理 自己封装工具函数 在处理 Ajax 的过程中,虽然有现成的库(比如 jQuery.ajax,axios 等),它毕竟是为了通用目的设计的,在使用的时候仍然不免繁琐。而在项目中,对 Api 进行调用的过程几乎都大同小异。如果设计得当,就连错误处理的方式都会是一样的。因此,在项目内的 Ajax 调用其实可以进行进一步的封装,使之在项目内使用起来更方便。如果接口方式发生变化,修改起来也更容易。 比如,当前接口要求使用 POST 方法调用(暂不考虑 RESTful),参数必须包括 action,返回的数据以 JSON 方式提供,如果出错,只要不是服务器异常都会返回特定的 JSON 数据,包括一个不等于 0 的 code 和可选的 message 属性。 那么用 jQuery 写这么一个 Ajax 调用,大概是这样 const apiUrl = "http://api.some.com/"; jQuery .ajax(url, { type: "post", dataType: "json", data: { action: "login", username: "uname", password: "passwd" } }) .done(function(data) { if (data.code) { alert(data.message || "登录失败!"); } else { window.location.assign("home"); } }) .fail(function() { alert("服务器错误"); }); 初步封装 同一项目中,这样的 Ajax 调用,基本上只有 data 部分和 .done 回调中的 else 部分不同,所以进行一次封装会大大减少代码量,可以这样封装 function appAjax(action, params) { var deffered = $.Deferred(); jQuery .ajax(apiUrl, { type: "post", dataType: "json", data: $.extend({ action: action }, params) }) .done(function(data) { // 当 code 为 0 或省略时,表示没有错误, // 其它值表示错误代码 if (data.code) { if (data.message) { // 如果服务器返回了消息,那么向用户呈现消息 // resolve(null),表示不需要后续进行业务处理 alert(data.message); deffered.resolve(); } else { // 如果服务器没返回消息,那么把 data 丢给外面的业务处理 deferred.reject(data); } } else { // 正常返回数据的情况 deffered.resolve(data); } }) .fail(function() { // Ajax 调用失败,向用户呈现消息,同时不需要进行后续的业务处理 alert("服务器错误"); deffered.resolve(); }); return deferred.promise(); } 而业务层的调用就很简单了 appAjax("login", { username: "uname", password: "passwd" }).done(function(data) { if (data) { window.location.assign("home"); } }).fail(function() { alert("登录失败"); }); 更换 API 调用接口 上面的封装对调用接口和返回数据进行了统一处理,把大部分项目接口约定的内容都处理掉了,剩下在每次调用时需要处理的就是纯粹的业务。 现在项目组决定不用 jQuery 的 Ajax,而是采用 axios 来调用 API(axios 不见得就比 jQuery 好,这里只是举例),那么只需要修改一下 appAjax() 的实现即可。所有业务调用都不需要修改。 假设现在的目标环境仍然是 ES5,那么需要第三方 Promise 提供,这里拟用 Bluebird,兼容原生 Promise 接口(在 HTML 中引入,未直接出现在 JS 代码中)。 function appAjax(action, params) { var deffered = $.Deferred(); axios .post(apiUrl, { data: $.extend({ action: action }, params) }) .then(function(data) { ... }, function() { ... }); return deferred.promise(); } 这次的封装采用了 axios 来实现 Web Api 调用。但是为了保持原来的接口(jQuery Promise 对象有提供 .done()、.fail() 和 .always() 事件处理),appAjax 仍然不得不返回 jQuery Promise。这样,即使所有地方都不再需要使用 jQuery,这里仍然得用。 项目中应该用还是不用 jQuery?请阅读为什么要用原生 JavaScript 代替 jQuery? 去除 jQuery 就只在这里使用 jQuery 总让人感觉如芒在背,想把它去掉。有两个办法 修改所有业务中的调用,去掉 .done()、.fail() 和 .always(),改成 .then()。这一步工作量较大,但基本无痛,因为 jQuery Promise 本身支持 .then()。但是有一点需要特别注意,这一点稍后说明 自己写个适配器,兼容 jQuery Promise 的接口,工作量也不小,但关键是要充分测试,避免差错。 上面提到第 1 种方法中有一点需要特别注意,那就是 .then() 和 .done() 系列函数在处理方式上有所不同。.then() 是按 Promise 的特性设计的,它返回的是另一个 Promise 对象;而 .done() 系列函数是按事件机制实现的,返回的是原来的 Promise 对象。所以像下面这样的代码在修改时就要注意了 appAjax(url, params) .done(function(data) { console.log("第 1 处处理", data) }) .done(function(data) { console.log("第 2 处处理", data) }); // 第 1 处处理 {} // 第 2 处处理 {} 简单的把 .done() 改成 .then() 之后(注意不需要使用 Bluebird,因为 jQuery Promise 支持 .then()) appAjax(url, params) .then(function(data) { console.log("第 1 处处理", data); }) .then(function(data) { console.log("第 2 处处理", data); }); // 第 1 处处理 {} // 第 2 处处理 undefined 原因上面已经讲了,这里正确的处理方式是合并多个 done 的代码,或者在 .then() 处理函数中返回 data: appAjax(url, params) .then(function(data) { console.log("第 1 处处理", data); return data; }) .then(function(data) { console.log("第 2 处处理", data); }); 使用 Promise 接口改善设计 我们的 appAjax() 接口部分也可以设计成 Promise 实现,这是一个更通用的接口。既使用不用 ES2015+ 特性,也可以使用像 jQuery Promise 或 Bluebird 这样的三方库提供的 Promise。 function appAjax(action, params) { // axios 依赖于 Promise,ES5 中可以使用 Bluebird 提供的 Promise return axios .post(apiUrl, { data: $.extend({ action: action }, params) }) .then(function(data) { // 这里调整了判断顺序,会让代码看起来更简洁 if (!data.code) { return data; } if (!data.message) { throw data; } alert(data.message); }, function() { alert("服务器错误"); }); } 不过现在前端有构建工具,可以使用 ES2015+ 配置 Babel,也可以使用 TypeScript …… 总之,选择很多,写起来也很方便。那么在设计的时候就不用局限于 ES5 所支持的内容了。所以可以考虑用 Promise + async/await 来实现 async function appAjax(action, params) { // axios 依赖于 Promise,ES5 中可以使用 Bluebird 提供的 Promise const data = await axios .post(apiUrl, { data: $.extend({ action: action }, params) }) // 这里模拟一个包含错误消息的结果,以便后面统一处理错误 // 这样就不需要用 try ... catch 了 .catch(() => ({ code: -1, message: "服务器错误" })); if (!data.code) { return data; } if (!data.message) { throw data; } alert(data.message); } 上面代码中使用 .catch() 来避免 try ... catch ... 的技巧在从不用 try-catch 实现的 async/await 语法说错误处理中提到过。 当然业务层调用也可以使用 async/await(记得写在 async 函数中): const data = await appAjax("login", { username: "uname", password: "passwd" }).catch(() => { alert("登录失败"); }); if (data) { window.location.assign("home"); } 对于多次 .done() 的改造: const data = await appAjax(url, params); console.log("第 1 处处理", data); console.log("第 2 处处理", data); 小结 本文以封装 Ajax 调用为例,看似在讲述异步调用。但实际想告诉大家的东西是:如何将一个常用的功能封装起来,实现代码重用和更简洁的调用;以及在封装的过程中需要考虑的问题——向前和向后的兼容性,在做工具函数封装的时候,应该尽量避免和某个特定的工具特性绑定,向公共标准靠拢——不知大家是否有所体会。

优秀的个人博客,低调大师

STS Python_SDK授权临时用户读写OSS资源

名词解释 RAM (Resource Access Management)和STS(Security Token Service)是阿里云提供的权限管理系统。RAM主要的作用是控制账号系统的权限。您可以使用RAM在主账号的权限范围内创建子用户,给不同的子用户分配不同的权限从而达到授权管理的目的。STS是一个安全凭证(Token)的管理系统。您可以使用STS来完成对于临时用户的访问授权。 子账号(RAM account):从阿里云的主账号中创建出来的子账号,在创建的时候可以分配独立的密码和权限,每个子账号拥有自己AccessKey,可以和阿里云主账号一样正常的完成有权限的操作。一般来说,这里的子账号可以理解为具有某种权限的用户,可以被认为是一个具有某些权限的操作发起者。 角色(Role):表示某种操作权限的虚拟概念,但是没有独立的登录密码和AccessKey。说明子账号可以扮演角色,扮演角色时候的权限是该角色自身的权限。 授权策略(Policy):用来定义权限的规则,比如允许用户读取或写入某些资源。 资源(Resource):代表用户可访问的云资源,比如OSS所有的Bucket、OSS的某个Bucket,或OSS的某个Bucket下面的某个Object等。 扮演角色(Assume role)扮演角色是实体用户获取角色身份的安全令牌的方法。一个实体用户调用STS APIAssumeRole可以获得角色的安全令牌,使用安全令牌可以访问云服务API。 这里将手动定义授权策略(Policy),将授权策略授权给角色 ,然后子账号(RAM account)通过扮演角色_方法 _获取 角色 的 安全令牌对 资源 进行操作.RAM 用户可以使用 API 扮演 RAM 角色。当 RAM 用户被授予AliyunSTSAssumeRoleAccess权限策略之后,可以使用其访问密钥调用 STS APIAssumeRole接口,以获取某个角色的安全令牌,从而使用安全令牌访问资源。 新建用户,角色,授权策略 新建RAM用户 登录RAM 控制台。 在左侧导航栏的人员管理菜单下,单击用户。 单击新建用户,输入登录名称和显示名称。说明单击添加用户,可一次性创建多个 RAM 用户。 在访问方式区域下,选择控制台密码登录或编程访问。 控制台密码登录:可以完成对登录安全的基本设置,包括自动生成或自定义登录密码、是否要求下次登录时重置密码以及是否要求开启多因素认证。 编程访问:将会自动为 RAM 用户创建访问密钥(AccessKey)。RAM 用户可以通过 API 或其他开发工具访问阿里云。 说明为了保障账号安全,建议仅为 RAM 用户选择一种登录方式。避免 RAM 用户离开组织后仍可以通过访问密钥访问阿里云资源。 单击确认。 授权RAM用户AssumeRole接口权限 登录RAM 控制台。 在左侧导航栏的人员管理菜单下,单击用户。 在用户列表中找到刚才创建的用户,单击列表【操作】栏下的【添加权限】 在弹出对话框中的【系统权限策略】搜索并添加_AliyunSTSAssumeRoleAccess_ 单击确认。 新建自定义权限策略test_policy 登录RAM 控制台。 在左侧导航栏的权限管理菜单下,单击权限策略管理。 单击新建权限策略。 填写策略名称和备注。 配置模式选择可视化配置或脚本配置。 若选择可视化配置:单击添加授权语句,根据界面提示,对权限效力、操作名称和资源等进行配置。 若选择脚本配置,请参考语法结构编辑策略内容。 单击确认。 下面为OSS所有权限语法示例 { "Statement": [ { "Action": "oss:*", "Effect": "Allow", "Resource": "*" } ], "Version": "1" } 新建角色testRole 注:这里角色有三种类型,创建可信实体为阿里云账号的RAM角色 云账号登录RAM控制台。 在左侧导航栏,单击RAM角色管理。 单击新建RAM角色。 选择可信实体类型为阿里云账号,单击下一步。 输入角色名称和备注。 选择云账号后,单击完成。 将自定义权限策略授权给角色 登录RAM 控制台。 在左侧导航栏的权限管理菜单下,单击授权。 单击新增授权。 在被授权主体区域下,输入 RAM 角色名称后,单击需要授权的 RAM 角色。 在左侧权限策略名称列表下,单击需要授予 RAM 角色的权限策略。说明在右侧区域框,选择某条策略并单击 ×,可撤销该策略。 单击确定。 完成授权后可在角色管理中查看授权详情 注:这里策略主体类型有两种,权限策略也可直接授权给用户 通过STS完成临时授权 STS基本概念 阿里云临时安全令牌(Security Token Service,STS)是阿里云提供的一种临时访问权限管理服务。 RAM角色(RAM role)一种虚拟的RAM用户。RAM角色的全局资源描述符(Role ARN)Role ARN是角色的全局资源描述符(Aliyun Resource Name,简称ARN),用来指定具体角色。每个角色都有一个唯一的全局资源描述符。格式:acs:ram::$accountID:role/$roleName。 可信实体(Trusted entity)角色的可信实体是指可以扮演角色的实体用户身份。创建角色时必须指定可信实体,角色只能被受信的主体扮演。可信实体可以是受信的阿里云账号、受信的阿里云服务或身份提供商。 扮演角色(Assume role)扮演角色是实体用户获取角色身份的安全令牌的方法。一个实体用户调用STS APIAssumeRole可以获得角色的安全令牌,使用安全令牌可以访问云服务API。 注:这里需要区分下RAM角色与扮演角色的区别,可信实体通过扮演角色_方法___获取 RAM角色的安全令牌 扮演角色的API接口概览 STS提供API调用接口每个请求都需要指定如下信息: 要执行的操作:Action参数。 每个操作接口都需要包含的公共请求参数。 操作接口所特有的请求参数。 调用AssumeRole接口获取一个临时身份。参考API RoleArn表示的是需要扮演的角色ID,角色的ID可以在角色管理>角色详情中找到。 RoleSessionName是一个用来标示临时凭证的名称,一般来说建议使用不同的应用程序用户来区分。 Policy表示的是在扮演角色的时候额外加上的一个权限限制。此参数可以限制生成的STS token的权限,若不指定则返回的token拥有指定角色的所有权限。 DurationSeconds指的是临时凭证的有效期,单位是s,最小为900,最大为3600。 id和secret表示的是需要扮演角色的子账号的AccessKey。 AssumeRole接口请求示例 https://sts.aliyuncs.com?Action=AssumeRole &RoleArn=acs:ram::123456789012****:role/adminrole &RoleSessionName=alice &DurationSeconds=3600 &Policy=<url_encoded_policy> &<公共请求参数> AssumeRole接口返回格式(json) { "Credentials": { "AccessKeyId": "STS.L4aBSCSJVMuKg5U1****", "AccessKeySecret": "wyLTSmsyPGP1ohvvw8xYgB29dlGI8KMiH2pK****", "Expiration": "2015-04-09T11:52:19Z", "SecurityToken": "********" }, "AssumedRoleUser": { "arn": "acs:sts::123456789012****:assumed-role/AdminRole/alice", "AssumedRoleUserId":"34458433936495****:alice" }, "RequestId": "6894B13B-6D71-4EF5-88FA-F32781734A7F" } 通过STS_SDK完成角色扮演 这里将通过官方提供的SDK模块进行具体的角色扮演 安装相关SDK包 pip install oss2pip install aliyun-python-sdk-corepip install aliyun-python-sdk-stssts sdkgithub 地址 获取临时身份信息 # 在控制台将 AliyunSTSAssumeRoleAccess 权限授权给子用户testRole,testRole操作AssumeRole接口,获取临时身份 def fetch_sts_info(access_key_id, access_key_secret, sts_role_arn): """子用户角色扮演获取临时身份的信息 :param access_key_id: 子用户的 access key id :param access_key_secret: 子用户的 access key secret :param sts_role_arn: STS角色的Arn :return StsInfo 返回临时身份信息对象 """ # 配置要访问的STS endpoint REGIONID = 'cn-hongkong' ENDPOINT = 'sts.cn-hongkong.aliyuncs.com' region_provider.add_endpoint('Sts', REGIONID, ENDPOINT) clt = client.AcsClient(access_key_id, access_key_secret, 'cn-hongkong') request = AssumeRoleRequest.AssumeRoleRequest() #request.set_accept_format('json') #指定角色ARN request.set_RoleArn(sts_role_arn) #设置会话名称,审计服务使用此名称区分调用者 request.set_RoleSessionName('oss-python-sdk-example') #发起请求,并得到response response = clt.do_action_with_exception(request) #格式化输出返回结果,将字符串结果转化为字典类型 i = json.loads(oss2.to_unicode(response)) #实例化StsInfo类,并将通过sts获取的临时身份信息存入 global StsInfo StsInfo = StsInfo() StsInfo.access_key_id = i['Credentials']['AccessKeyId'] StsInfo.access_key_secret = i['Credentials']['AccessKeySecret'] StsInfo.security_token = i['Credentials']['SecurityToken'] StsInfo.request_id = i['RequestId'] StsInfo.expiration = oss2.utils.to_unixtime(i['Credentials']['Expiration'], '%Y-%m-%dT%H:%M:%SZ') #返回StsInfo对象 return StsInfo 将临时身份信息存入json文件 根据需求,可将临时身份信息存储到json文件中,等到临时身份过期后再重新请求,避免重复请求,造成临时身份泛滥,引发资源匮乏与安全隐患 def save_info(): #存储临时身份信息 with open('StsInfo.json','w',encoding='utf-8') as f: data = {'sts_key_id':StsInfo.access_key_id,'sts_key_secret':StsInfo.access_key_secret,'sts_secrity_token':StsInfo.security_token,'sts_expire_date':StsInfo.expiration,'sts_reques_id':StsInfo.request_id} json.dump(data,f,ensure_ascii=False) f.close def open_info(): #读取临时身份信息 with open('./StsInfo.json','r',encoding='utf-8') as f: global STSINFO STSINFO = json.load(f) return STSINFO 通过临时身份操作Bucket资源 def buck_put_object(): #打印验证临时身份信息 print(StsInfo) print('key id:',StsInfo.access_key_id) print("key_secret:",StsInfo.access_key_secret) print("secrity_token:",StsInfo.security_token) print("request_id:",StsInfo.request_id) print("expiration:",StsInfo.expiration) #实例化Bucket对象,并上传字符串 auth = oss2.StsAuth(StsInfo.access_key_id, StsInfo.access_key_secret, StsInfo.security_token) bucket = oss2.Bucket(auth,endpoint,'fralychen') result = bucket.put_object('fralychen','good good study day day up') 全部代码 # -*- conding:utf-8 -*- import json import os import oss2 from aliyunsdkcore import client from aliyunsdkcore.profile import region_provider from aliyunsdksts.request.v20150401 import AssumeRoleRequest ##定义一些变量 access_key_id = 'LTAI4FoMe6umpCSFQdEC9neg' access_key_secret = 'jgEvtFOqIAGqKZve7zMvg8dJhSZv9J' bucket_name = 'fralychen' endpoint = 'oss-cn-hongkong.aliyuncs.com' sts_role_arn = 'acs:ram::1149877324567510:role/testrole' # 确认上面的参数都填写正确了 for param in (access_key_id, access_key_secret, bucket_name, endpoint, sts_role_arn): assert '<' not in param, '请设置参数:' + param #创建StsToken类方便用来存储临时身份信息 class StsInfo(object): """AssumeRole返回的临时身份信息 :param str access_key_id: 临时身份的access key id :param str access_key_secret: 临时身份的access key secret :param int expiration: 过期时间,UNIX时间,自1970年1月1日UTC零点的秒数 :param str security_token: 临时身份Token :param str request_id: 请求ID """ def __init__(self): self.access_key_id = '' self.access_key_secret = '' self.expiration = 0 self.security_token = '' self.request_id = '' # 在控制台将 AliyunSTSAssumeRoleAccess 权限授权给RAM子用户之后才能通过RAM用户获取临时身份信息 def fetch_sts_info(access_key_id, access_key_secret, sts_role_arn): """子用户角色扮演获取临时身份的信息 :param access_key_id: 子用户的 access key id :param access_key_secret: 子用户的 access key secret :param sts_role_arn: STS角色的Arn :return StsInfo 返回临时身份信息对象 """ # 配置要访问的STS endpoint _REGIONID = 'cn-hongkong' _ENDPOINT = 'sts.cn-hongkong.aliyuncs.com' region_provider.add_endpoint('Sts', _REGIONID, _ENDPOINT) clt = client.AcsClient(access_key_id, access_key_secret, 'cn-hongkong') request = AssumeRoleRequest.AssumeRoleRequest() #request.set_accept_format('json') #指定角色ARN request.set_RoleArn(sts_role_arn) #设置会话名称,审计服务使用此名称区分调用者 request.set_RoleSessionName('oss-python-sdk-example') #设置临时身份过期时间 request.set_DurationSeconds(DurationSeconds) #发起请求,并得到response response = clt.do_action_with_exception(request) #格式化输出返回结果,将字符串结果转化为字典类型 i = json.loads(oss2.to_unicode(response)) #实例化StsInfo类并将临时身份信息存入对象 global StsInfo StsInfo = StsInfo() StsInfo.access_key_id = i['Credentials']['AccessKeyId'] StsInfo.access_key_secret = i['Credentials']['AccessKeySecret'] StsInfo.security_token = i['Credentials']['SecurityToken'] StsInfo.request_id = i['RequestId'] StsInfo.expiration = oss2.utils.to_unixtime(i['Credentials']['Expiration'], '%Y-%m-%dT%H:%M:%SZ') #存储临时身份信息 save_info() #使用sts授权的临时身份上传文件到bucket def buck_put_object(sts_key_id, sts_key_secret, sts_secrity_token): """上传字符串到资源 :param sts_key_id: 临时身份的 access key id :param sts_key_secret: 临时身份的 access key secret :param sts_secrity_token: 临时身份的 secrity token :retu """ #实例化Bucket对象,并上传字符串 auth = oss2.StsAuth(sts_key_id, sts_key_secret, sts_secrity_token) bucket = oss2.Bucket(auth,endpoint,'fralychen') result = bucket.put_object('fralychen','good good study day day up') #根据需求,可将临时身份信息存储到json文件中,等到临时身份过期后再重新请求,避免重复请求,用户泛滥 def save_info(): #存储临时身份信息 with open('StsInfo.json','w',encoding='utf-8') as f: data = {'sts_key_id':StsInfo.access_key_id,'sts_key_secret':StsInfo.access_key_secret,'sts_secrity_token':StsInfo.security_token,'sts_expire_date':StsInfo.expiration,'sts_reques_id':StsInfo.request_id} json.dump(data,f,ensure_ascii=False) f.close def open_info(): #读取临时身份信息 with open('./StsInfo.json','r',encoding='utf-8') as f: global STSINFO STSINFO = json.load(f) return STSINFO #定义临时身份过期时间 DurationSeconds = 900 try: open_info() except IOError: print("Error: 没有用户信息文件或文件读取失败") print("初始化身份信息,临时身份信息存储中....") fetch_sts_info(access_key_id, access_key_secret, sts_role_arn) print("临时身份信息存储完毕,当前目录下StsInfo.json") else: if oss2.utils.http_to_unixtime(oss2.utils.http_date()) + DurationSeconds > STSINFO["sts_expire_date"]: buck_put_object(sts_key_id = STSINFO["sts_key_id"],sts_key_secret = STSINFO["sts_key_secret"], sts_secrity_token = STSINFO["sts_secrity_token"]) print("上传成功,good_lucky") else: print("更新临时用户信息,请稍后") fetch_sts_info(access_key_id, access_key_secret, sts_role_arn) buck_put_object(sts_key_id = STSINFO["sts_key_id"],sts_key_secret = STSINFO["sts_key_secret"], sts_secrity_token = STSINFO["sts_secrity_token"]) print("上传成功,good_lucky")

优秀的个人博客,低调大师

【Python初级】StringIO和BytesIO读写操作的小思考

from io import StringIO; f = StringIO(); f.write('Hello World'); s = f.readline(); print s; 上面这种方法“无论如何”都读不出f的内容,使用readlines和循环也不行。 但是,用以下的方法,却可以“正常读取”: from io import StringIO; f = StringIO('Hello World'); s = f.readline(); print s; 这是为什么呢? 这是因为the stream position的原因,当你用: d = StringIO('Hello World') 其stream position为0(可以通过d.tell()获得),而后执行: d.readline() stream position移动到11.因此当我们再次执行d.readline()时,返回的是空字符串。演示见图: 类似的,使用: f = StringIO() stream position也为0,但执行了: f.write('Hello World') 之后,stream position就移动到11了,因此此时你再执行readline时返回的依旧是空字符串。 当然咯,既然这个读取是和stream position的位置有关系,那么要能够在当前情况下还能读取'Hello World!',我们可以调整这个指针的位置,执行: f.seek(0) 再进行读取操作,即可。 下面利用BytesIO进行演示,是一样的道理:

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。