• 关于 python 替换t 的搜索结果

回答

一、文件的打开和创建 ? 12345 f = open('/tmp/test.txt')f.read()'hello python!nhello world!n'f 二、文件的读取步骤:打开 -- 读取 -- 关闭 ? 1234 f = open('/tmp/test.txt')f.read() 'hello python!nhello world!n' f.close() 读取数据是后期数据处理的必要步骤。.txt是广泛使用的数据文件格式。一些.csv, .xlsx等文件可以转换为.txt 文件进行读取。我常使用的是Python自带的I/O接口,将数据读取进来存放在list中,然后再用numpy科学计算包将list的数据转换为array格式,从而可以像MATLAB一样进行科学计算。 下面是一段常用的读取txt文件代码,可以用在大多数的txt文件读取中 ? 12345678910111213141516 filename = 'array_reflection_2D_TM_vertical_normE_center.txt' # txt文件和当前脚本在同一目录下,所以不用写具体路径pos = []Efield = []with open(filename, 'r') as file_to_read: while True: lines = file_to_read.readline() # 整行读取数据 if not lines: break pass p_tmp, E_tmp = [float(i) for i in lines.split()] # 将整行数据分割处理,如果分割符是空格,括号里就不用传入参数,如果是逗号, 则传入‘,'字符。 pos.append(p_tmp) # 添加新读取的数据 Efield.append(E_tmp) pass pos = np.array(pos) # 将数据从list类型转换为array类型。 Efield = np.array(Efield) pass 例如下面是将要读入的txt文件 2016626171647895.png (429×301) 经过读取后,在Enthought Canopy的variable window查看读入的数据, 左侧为pos,右侧为Efield。 2016626171713978.png (148×277)2016626171743777.png (147×280) 三、文件写入(慎重,小心别清空原本的文件)步骤:打开 -- 写入 -- (保存)关闭 直接的写入数据是不行的,因为默认打开的是'r' 只读模式 ? 123456 f.write('hello boy')Traceback (most recent call last): File "", line 1, in IOError: File not open for writing f 应该先指定可写的模式 ? 12 f1 = open('/tmp/test.txt','w')f1.write('hello boy!') 但此时数据只写到了缓存中,并未保存到文件,而且从下面的输出可以看到,原先里面的配置被清空了 ? 12 [root@node1 ~]# cat /tmp/test.txt[root@node1 ~]# 关闭这个文件即可将缓存中的数据写入到文件中 ? 123 f1.close() [root@node1 ~]# cat /tmp/test.txt[root@node1 ~]# hello boy! 注意:这一步需要相当慎重,因为如果编辑的文件存在的话,这一步操作会先清空这个文件再重新写入。那么如果不要清空文件再写入该如何做呢? 使用r+ 模式不会先清空,但是会替换掉原先的文件,如下面的例子:hello boy! 被替换成hello aay! ? 12345 f2 = open('/tmp/test.txt','r+')f2.write('nhello aa!')f2.close() [root@node1 python]# cat /tmp/test.txthello aay! 如何实现不替换? ? 12345678 f2 = open('/tmp/test.txt','r+')f2.read() 'hello girl!' f2.write('nhello boy!')f2.close() [root@node1 python]# cat /tmp/test.txthello girl!hello boy! 可以看到,如果在写之前先读取一下文件,再进行写入,则写入的数据会添加到文件末尾而不会替换掉原先的文件。这是因为指针引起的,r+ 模式的指针默认是在文件的开头,如果直接写入,则会覆盖源文件,通过read() 读取文件后,指针会移到文件的末尾,再写入数据就不会有问题了。这里也可以使用a 模式 ? 12345678 f = open('/tmp/test.txt','a')f.write('nhello man!')f.close() [root@node1 python]# cat /tmp/test.txthello girl!hello boy!hello man! 关于其他模式的介绍,见下表: 2016626170852899.png (713×317) 文件对象的方法:f.readline() 逐行读取数据 方法一: ? 123456789 f = open('/tmp/test.txt')f.readline() 'hello girl!n' f.readline() 'hello boy!n' f.readline() 'hello man!' f.readline() '' 方法二: ? 123456789101112 for i in open('/tmp/test.txt'): ... print i...hello girl!hello boy!hello man!f.readlines() 将文件内容以列表的形式存放 f = open('/tmp/test.txt')f.readlines() ['hello girl!n', 'hello boy!n', 'hello man!'] f.close() f.next() 逐行读取数据,和f.readline() 相似,唯一不同的是,f.readline() 读取到最后如果没有数据会返回空,而f.next() 没读取到数据则会报错 ? 12345678910111213141516 f = open('/tmp/test.txt')f.readlines() ['hello girl!n', 'hello boy!n', 'hello man!'] f.close() f = open('/tmp/test.txt')f.next() 'hello girl!n' f.next() 'hello boy!n' f.next() 'hello man!' f.next() Traceback (most recent call last):File "", line 1, in StopIteration f.writelines() 多行写入 ? 1234567891011 l = ['nhello dear!','nhello son!','nhello baby!n']f = open('/tmp/test.txt','a')f.writelines(l)f.close() [root@node1 python]# cat /tmp/test.txthello girl!hello boy!hello man!hello dear!hello son!hello baby! f.seek(偏移量,选项) ? 12345678910111213141516 f = open('/tmp/test.txt','r+')f.readline() 'hello girl!n' f.readline() 'hello boy!n' f.readline() 'hello man!n' f.readline() ' ' f.close()f = open('/tmp/test.txt','r+')f.read() 'hello girl!nhello boy!nhello man!n' f.readline() '' f.close() 这个例子可以充分的解释前面使用r+这个模式的时候,为什么需要执行f.read()之后才能正常插入f.seek(偏移量,选项)(1)选项=0,表示将文件指针指向从文件头部到“偏移量”字节处 (2)选项=1,表示将文件指针指向从文件的当前位置,向后移动“偏移量”字节 (3)选项=2,表示将文件指针指向从文件的尾部,向前移动“偏移量”字节 偏移量:正数表示向右偏移,负数表示向左偏移 ? 12345678910111213 f = open('/tmp/test.txt','r+')f.seek(0,2)f.readline() '' f.seek(0,0)f.readline() 'hello girl!n' f.readline() 'hello boy!n' f.readline() 'hello man!n' f.readline() '' f.flush() 将修改写入到文件中(无需关闭文件) ? 12 f.write('hello python!')f.flush() ? 1 [root@node1 python]# cat /tmp/test.txt ? 1234 hello girl!hello boy!hello man!hello python! f.tell() 获取指针位置 ? 123456789 f = open('/tmp/test.txt')f.readline() 'hello girl!n' f.tell() 12 f.readline() 'hello boy!n' f.tell() 23 四、内容查找和替换1、内容查找实例:统计文件中hello个数 思路:打开文件,遍历文件内容,通过正则表达式匹配关键字,统计匹配个数。 ? 1 [root@node1 ~]# cat /tmp/test.txt ? 1234 hello girl!hello boy!hello man!hello python! 脚本如下: 方法一: ? 12345678910 !/usr/bin/python import ref = open('/tmp/test.txt')source = f.read()f.close()r = r'hello's = len(re.findall(r,source))print s[root@node1 python]# python count.py4 方法二: ? 123456789101112 !/usr/bin/python import refp = file("/tmp/test.txt",'r')count = 0for s in fp.readlines():li = re.findall("hello",s)if len(li)>0:count = count + len(li)print "Search",count, "hello"fp.close()[root@node1 python]# python count1.pySearch 4 hello 2、替换实例:把test.txt 中的hello全部换为"hi",并把结果保存到myhello.txt中。 ? 1234567891011121314 !/usr/bin/python import ref1 = open('/tmp/test.txt')f2 = open('/tmp/myhello.txt','r+')for s in f1.readlines():f2.write(s.replace('hello','hi'))f1.close()f2.close()[root@node1 python]# touch /tmp/myhello.txt[root@node1 ~]# cat /tmp/myhello.txthi girl!hi boy!hi man!hi python! 实例:读取文件test.txt内容,去除空行和注释行后,以行为单位进行排序,并将结果输出为result.txt。test.txt 的内容如下所示: ? 12345678910111213141516171819 some words Sometimes in life,You find a special friend;Someone who changes your life just by being part of it.Someone who makes you laugh until you can't stop;Someone who makes you believe that there really is good in the world.Someone who convinces you that there really is an unlocked door just waiting for you to open it.This is Forever Friendship.when you're down,and the world seems dark and empty,Your forever friend lifts you up in spirits and makes that dark and empty worldsuddenly seem bright and full.Your forever friend gets you through the hard times,the sad times,and the confused times.If you turn and walk away,Your forever friend follows,If you lose you way,Your forever friend guides you and cheers you on.Your forever friend holds your hand and tells you that everything is going to be okay. 脚本如下: ? 12345678910 f = open('cdays-4-test.txt')result = list()for line in f.readlines(): # 逐行读取数据line = line.strip() #去掉每行头尾空白if not len(line) or line.startswith('#'): # 判断是否是空行或注释行continue #是的话,跳过不处理result.append(line) #保存result.sort() #排序结果print resultopen('cdays-4-result.txt','w').write('%s' % 'n'.join(result))

xuning715 2019-12-02 01:10:18 0 浏览量 回答数 0

回答

Python常见数据结构整理 Python中常见的数据结构可以统称为容器(container)。序列(如列表和元组)、映射(如字典)以及集合(set)是三类主要的容器。 一、序列(列表、元组和字符串) 序列中的每个元素都有自己的编号。Python中有6种内建的序列。其中列表和元组是最常见的类型。其他包括字符串、Unicode字符串、buffer对象和xrange对象。下面重点介绍下列表、元组和字符串。 1、列表 列表是可变的,这是它区别于字符串和元组的最重要的特点,一句话概括即:列表可以修改,而字符串和元组不能。 (1)、创建 通过下面的方式即可创建一个列表: 1 2 3 4 list1=['hello','world'] print list1 list2=[1,2,3] print list2 输出: ['hello', 'world'] [1, 2, 3] 可以看到,这中创建方式非常类似于javascript中的数组。 (2)、list函数 通过list函数(其实list是一种类型而不是函数)对字符串创建列表非常有效: 1 2 list3=list("hello") print list3 输出: ['h', 'e', 'l', 'l', 'o'] 2、元组 元组与列表一样,也是一种序列,唯一不同的是元组不能被修改(字符串其实也有这种特点)。 (1)、创建 1 2 3 4 5 6 t1=1,2,3 t2="jeffreyzhao","cnblogs" t3=(1,2,3,4) t4=() t5=(1,) print t1,t2,t3,t4,t5 输出: (1, 2, 3) ('jeffreyzhao', 'cnblogs') (1, 2, 3, 4) () (1,) 从上面我们可以分析得出: a、逗号分隔一些值,元组自动创建完成; b、元组大部分时候是通过圆括号括起来的; c、空元组可以用没有包含内容的圆括号来表示; d、只含一个值的元组,必须加个逗号(,); (2)、tuple函数 tuple函数和序列的list函数几乎一样:以一个序列(注意是序列)作为参数并把它转换为元组。如果参数就算元组,那么该参数就会原样返回: 1 2 3 4 5 6 7 8 t1=tuple([1,2,3]) t2=tuple("jeff") t3=tuple((1,2,3)) print t1 print t2 print t3 t4=tuple(123) print t45 输出: (1, 2, 3) ('j', 'e', 'f', 'f') (1, 2, 3) Traceback (most recent call last): File "F:\Python\test.py", line 7, in <module> t4=tuple(123) TypeError: 'int' object is not iterable 3、字符串 (1)创建 1 2 3 4 5 str1='Hello world' print str1 print str1[0] for c in str1: print c 输出: Hello world H H e l l o w o r l d (2)格式化 字符串格式化使用字符串格式化操作符即百分号%来实现。 1 2 str1='Hello,%s' % 'world.' print str1 格式化操作符的右操作数可以是任何东西,如果是元组或者映射类型(如字典),那么字符串格式化将会有所不同。 1 2 3 4 5 6 strs=('Hello','world') #元组 str1='%s,%s' % strs print str1 d={'h':'Hello','w':'World'} #字典 str1='%(h)s,%(w)s' % d print str1 输出: Hello,world Hello,World 注意:如果需要转换的元组作为转换表达式的一部分存在,那么必须将它用圆括号括起来: 1 2 str1='%s,%s' % 'Hello','world' print str1 输出: Traceback (most recent call last): File "F:\Python\test.py", line 2, in <module> str1='%s,%s' % 'Hello','world' TypeError: not enough arguments for format string 如果需要输出%这个特殊字符,毫无疑问,我们会想到转义,但是Python中正确的处理方式如下: 1 2 str1='%s%%' % 100 print str1 输出:100% 对数字进行格式化处理,通常需要控制输出的宽度和精度: 1 2 3 4 5 6 7 from math import pi str1='%.2f' % pi #精度2 print str1 str1='%10f' % pi #字段宽10 print str1 str1='%10.2f' % pi #字段宽10,精度2 print str1 输出: 3.14 3.141593 3.14 字符串格式化还包含很多其他丰富的转换类型,可参考官方文档。 Python中在string模块还提供另外一种格式化值的方法:模板字符串。它的工作方式类似于很多UNIX Shell里的变量替换,如下所示: 1 2 3 4 from string import Template str1=Template('$x,$y!') str1=str1.substitute(x='Hello',y='world') print str1 输出: Hello,world! 如果替换字段是单词的一部分,那么参数名称就必须用括号括起来,从而准确指明结尾: 1 2 3 4 from string import Template str1=Template('Hello,w${x}d!') str1=str1.substitute(x='orl') print str1 输出: Hello,world! 如要输出符,可以使用$输出: 1 2 3 4 from string import Template str1=Template('$x$$') str1=str1.substitute(x='100') print str1 输出:100$ 除了关键字参数之外,模板字符串还可以使用字典变量提供键值对进行格式化: 1 2 3 4 5 from string import Template d={'h':'Hello','w':'world'} str1=Template('$h,$w!') str1=str1.substitute(d) print str1 输出: Hello,world! 除了格式化之外,Python字符串还内置了很多实用方法,可参考官方文档,这里不再列举。 4、通用序列操作(方法) 从列表、元组以及字符串可以“抽象”出序列的一些公共通用方法(不是你想像中的CRUD),这些操作包括:索引(indexing)、分片(sliceing)、加(adding)、乘(multiplying)以及检查某个元素是否属于序列的成员。除此之外,还有计算序列长度、最大最小元素等内置函数。 (1)索引 1 2 3 4 5 6 str1='Hello' nums=[1,2,3,4] t1=(123,234,345) print str1[0] print nums[1] print t1[2] 输出 H 2 345 索引从0(从左向右)开始,所有序列可通过这种方式进行索引。神奇的是,索引可以从最后一个位置(从右向左)开始,编号是-1: 1 2 3 4 5 6 str1='Hello' nums=[1,2,3,4] t1=(123,234,345) print str1[-1] print nums[-2] print t1[-3] 输出: o 3 123 (2)分片 分片操作用来访问一定范围内的元素。分片通过冒号相隔的两个索引来实现: 1 2 3 4 5 6 7 8 nums=range(10) print nums print nums[1:5] print nums[6:10] print nums[1:] print nums[-3:-1] print nums[-3:] #包括序列结尾的元素,置空最后一个索引 print nums[:] #复制整个序列 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2, 3, 4] [6, 7, 8, 9] [1, 2, 3, 4, 5, 6, 7, 8, 9] [7, 8] [7, 8, 9] 不同的步长,有不同的输出: 1 2 3 4 5 6 7 8 nums=range(10) print nums print nums[0:10] #默认步长为1 等价于nums[1:5:1] print nums[0:10:2] #步长为2 print nums[0:10:3] #步长为3 ##print nums[0:10:0] #步长为0 print nums[0:10:-2] #步长为-2 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 2, 4, 6, 8] [0, 3, 6, 9] [] (3)序列相加 1 2 3 4 5 6 7 str1='Hello' str2=' world' print str1+str2 num1=[1,2,3] num2=[2,3,4] print num1+num2 print str1+num1 输出: Hello world [1, 2, 3, 2, 3, 4] Traceback (most recent call last): File "F:\Python\test.py", line 7, in <module> print str1+num1 TypeError: cannot concatenate 'str' and 'list' objects (4)乘法 1 2 3 4 5 6 print [None]*10 str1='Hello' print str1*2 num1=[1,2] print num1*2 print str1*num1 输出: [None, None, None, None, None, None, None, None, None, None] HelloHello [1, 2, 1, 2] Traceback (most recent call last): File "F:\Python\test.py", line 5, in <module> print str1*num1 TypeError: can't multiply sequence by non-int of type 'list' (5)成员资格 in运算符会用来检查一个对象是否为某个序列(或者其他类型)的成员(即元素): 1 2 3 4 5 str1='Hello' print 'h' in str1 print 'H' in str1 num1=[1,2] print 1 in num1 输出: False True True (6)长度、最大最小值 通过内建函数len、max和min可以返回序列中所包含元素的数量、最大和最小元素。 1 2 3 4 5 6 7 8 str1='Hello' print len(str1) print max(str1) print min(str1) num1=[1,2,1,4,123] print len(num1) print max(num1) print min(num1) 输出: 5 o H 5 123 1 二、映射(字典) 映射中的每个元素都有一个名字,如你所知,这个名字专业的名称叫键。字典(也叫散列表)是Python中唯一内建的映射类型。 1、键类型 字典的键可以是数字、字符串或者是元组,键必须唯一。在Python中,数字、字符串和元组都被设计成不可变类型,而常见的列表以及集合(set)都是可变的,所以列表和集合不能作为字典的键。键可以为任何不可变类型,这正是Python中的字典最强大的地方。 1 2 3 4 5 6 7 8 list1=["hello,world"] set1=set([123]) d={} d[1]=1 print d d[list1]="Hello world." d[set1]=123 print d 输出: {1: 1} Traceback (most recent call last): File "F:\Python\test.py", line 6, in <module> d[list1]="Hello world." TypeError: unhashable type: 'list' 2、自动添加 即使键在字典中并不存在,也可以为它分配一个值,这样字典就会建立新的项。 3、成员资格 表达式item in d(d为字典)查找的是键(containskey),而不是值(containsvalue)。 Python字典强大之处还包括内置了很多常用操作方法,可参考官方文档,这里不再列举。 思考:根据我们使用强类型语言的经验,比如C#和Java,我们肯定会问Python中的字典是线程安全的吗。 三、集合 集合(Set)在Python 2.3引入,通常使用较新版Python可直接创建,如下所示: strs=set(['jeff','wong','cnblogs']) nums=set(range(10)) 看上去,集合就是由序列(或者其他可迭代的对象)构建的。集合的几个重要特点和方法如下: 1、副本是被忽略的 集合主要用于检查成员资格,因此副本是被忽略的,如下示例所示,输出的集合内容是一样的。 1 2 3 4 5 set1=set([0,1,2,3,0,1,2,3,4,5]) print set1 set2=set([0,1,2,3,4,5]) print set2 输出如下: set([0, 1, 2, 3, 4, 5]) set([0, 1, 2, 3, 4, 5]) 2、集合元素的顺序是随意的 这一点和字典非常像,可以简单理解集合为没有value的字典。 1 2 strs=set(['jeff','wong','cnblogs']) print strs 输出如下: set(['wong', 'cnblogs', 'jeff'])

琴瑟 2019-12-02 01:22:27 0 浏览量 回答数 0

问题

延迟评估/对数据结构中原子数据类型的引用

is大龙 2020-03-24 09:40:50 1 浏览量 回答数 1

新用户福利专场,云服务器ECS低至96.9元/年

新用户福利专场,云服务器ECS低至96.9元/年

问题

Python创建一个街道地址表

一码平川MACHEL 2019-12-01 19:32:15 558 浏览量 回答数 1

问题

替换为循环以提高我的python脚本的速度

is大龙 2020-03-24 19:10:31 0 浏览量 回答数 1

问题

创建子账号并成功授予权限后如何测试

云栖大讲堂 2019-12-01 20:55:05 1176 浏览量 回答数 0

回答

Python 正则表达式正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。re 模块使 Python 语言拥有全部的正则表达式功能。compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。本章节主要介绍Python中常用的正则表达式处理函数。re.match函数re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。函数语法:re.match(pattern, string, flags=0)函数参数说明:参数 描述pattern 匹配的正则表达式string 要匹配的字符串。flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志匹配成功re.match方法返回一个匹配的对象,否则返回None。我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。匹配对象方法 描述group(num=0) 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。实例 !/usr/bin/python -- coding: UTF-8 -- import reprint(re.match('www', 'www.runoob.com').span()) # 在起始位置匹配print(re.match('com', 'www.runoob.com')) # 不在起始位置匹配以上实例运行输出结果为:(0, 3)None实例 !/usr/bin/python import re line = "Cats are smarter than dogs" matchObj = re.match( r'(.) are (.?) .*', line, re.M|re.I) if matchObj: print "matchObj.group() : ", matchObj.group() print "matchObj.group(1) : ", matchObj.group(1) print "matchObj.group(2) : ", matchObj.group(2)else: print "No match!!"以上实例执行结果如下:matchObj.group() : Cats are smarter than dogsmatchObj.group(1) : CatsmatchObj.group(2) : smarterre.search方法re.search 扫描整个字符串并返回第一个成功的匹配。函数语法:re.search(pattern, string, flags=0)函数参数说明:参数 描述pattern 匹配的正则表达式string 要匹配的字符串。flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。匹配成功re.search方法返回一个匹配的对象,否则返回None。我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。匹配对象方法 描述group(num=0) 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。实例 !/usr/bin/python -- coding: UTF-8 -- import reprint(re.search('www', 'www.runoob.com').span()) # 在起始位置匹配print(re.search('com', 'www.runoob.com').span()) # 不在起始位置匹配以上实例运行输出结果为:(0, 3)(11, 14)实例 !/usr/bin/python import re line = "Cats are smarter than dogs"; searchObj = re.search( r'(.) are (.?) .*', line, re.M|re.I) if searchObj: print "searchObj.group() : ", searchObj.group() print "searchObj.group(1) : ", searchObj.group(1) print "searchObj.group(2) : ", searchObj.group(2)else: print "Nothing found!!"以上实例执行结果如下:searchObj.group() : Cats are smarter than dogssearchObj.group(1) : CatssearchObj.group(2) : smarterre.match与re.search的区别re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。实例 !/usr/bin/python import re line = "Cats are smarter than dogs"; matchObj = re.match( r'dogs', line, re.M|re.I)if matchObj: print "match --> matchObj.group() : ", matchObj.group()else: print "No match!!" matchObj = re.search( r'dogs', line, re.M|re.I)if matchObj: print "search --> matchObj.group() : ", matchObj.group()else: print "No match!!"以上实例运行结果如下:No match!!search --> matchObj.group() : dogs检索和替换Python 的 re 模块提供了re.sub用于替换字符串中的匹配项。语法:re.sub(pattern, repl, string, count=0, flags=0)参数:pattern : 正则中的模式字符串。repl : 替换的字符串,也可为一个函数。string : 要被查找替换的原始字符串。count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。实例 !/usr/bin/python -- coding: UTF-8 -- import re phone = "2004-959-559 # 这是一个国外电话号码" 删除字符串中的 Python注释 num = re.sub(r'#.*$', "", phone)print "电话号码是: ", num 删除非数字(-)的字符串 num = re.sub(r'D', "", phone)print "电话号码是 : ", num以上实例执行结果如下:电话号码是: 2004-959-559 电话号码是 : 2004959559repl 参数是一个函数以下实例中将字符串中的匹配的数字乘以 2:实例 !/usr/bin/python -- coding: UTF-8 -- import re 将匹配的数字乘以 2 def double(matched): value = int(matched.group('value')) return str(value * 2) s = 'A23G4HFD567'print(re.sub('(?Pd+)', double, s))执行输出结果为:A46G8HFD1134re.compile 函数compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。语法格式为:re.compile(pattern[, flags])参数:pattern : 一个字符串形式的正则表达式flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:re.I 忽略大小写re.L 表示特殊字符集 w, W, b, B, s, S 依赖于当前环境re.M 多行模式re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)re.U 表示特殊字符集 w, W, b, B, d, D, s, S 依赖于 Unicode 字符属性数据库re.X 为了增加可读性,忽略空格和 # 后面的注释实例实例 import repattern = re.compile(r'd+') # 用于匹配至少一个数字m = pattern.match('one12twothree34four') # 查找头部,没有匹配print m None m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配print m None m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配print m # 返回一个 Match 对象 <_sre.SRE_Match object at 0x10a42aac0> m.group(0) # 可省略 0 '12' m.start(0) # 可省略 0 3 m.end(0) # 可省略 0 5 m.span(0) # 可省略 0 (3, 5)在上面,当匹配成功时返回一个 Match 对象,其中:group([group1, …]) 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0);start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;span([group]) 方法返回 (start(group), end(group))。再看看一个例子:实例 import repattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写m = pattern.match('Hello World Wide Web')print m # 匹配成功,返回一个 Match 对象 <_sre.SRE_Match object at 0x10bea83e8> m.group(0) # 返回匹配成功的整个子串 'Hello World' m.span(0) # 返回匹配成功的整个子串的索引 (0, 11) m.group(1) # 返回第一个分组匹配成功的子串 'Hello' m.span(1) # 返回第一个分组匹配成功的子串的索引 (0, 5) m.group(2) # 返回第二个分组匹配成功的子串 'World' m.span(2) # 返回第二个分组匹配成功的子串 (6, 11) m.groups() # 等价于 (m.group(1), m.group(2), ...) ('Hello', 'World') m.group(3) # 不存在第三个分组 Traceback (most recent call last): File "", line 1, in IndexError: no such groupfindall在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。注意: match 和 search 是匹配一次 findall 匹配所有。语法格式为:findall(string[, pos[, endpos]])参数:string : 待匹配的字符串。pos : 可选参数,指定字符串的起始位置,默认为 0。endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。查找字符串中的所有数字:实例 -- coding:UTF8 -- import re pattern = re.compile(r'd+') # 查找数字result1 = pattern.findall('runoob 123 google 456')result2 = pattern.findall('run88oob123google456', 0, 10) print(result1)print(result2)输出结果:['123', '456']['88', '12']re.finditer和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。re.finditer(pattern, string, flags=0)参数:参数 描述pattern 匹配的正则表达式string 要匹配的字符串。flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志实例 -- coding: UTF-8 -- import re it = re.finditer(r"d+","12a32bc43jf3") for match in it: print (match.group() ) 输出结果:12 32 43 3re.splitsplit 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:re.split(pattern, string[, maxsplit=0, flags=0])参数:参数 描述pattern 匹配的正则表达式string 要匹配的字符串。maxsplit 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志实例 import rere.split('W+', 'runoob, runoob, runoob.')['runoob', 'runoob', 'runoob', '']re.split('(W+)', ' runoob, runoob, runoob.') ['', ' ', 'runoob', ', ', 'runoob', ', ', 'runoob', '.', ''] re.split('W+', ' runoob, runoob, runoob.', 1) ['', 'runoob, runoob, runoob.'] re.split('a*', 'hello world') # 对于一个找不到匹配的字符串而言,split 不会对其作出分割 ['hello world']正则表达式对象re.RegexObjectre.compile() 返回 RegexObject 对象。re.MatchObjectgroup() 返回被 RE 匹配的字符串。start() 返回匹配开始的位置end() 返回匹配结束的位置span() 返回一个元组包含匹配 (开始,结束) 的位置正则表达式修饰符 - 可选标志正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:修饰符 描述re.I 使匹配对大小写不敏感re.L 做本地化识别(locale-aware)匹配re.M 多行匹配,影响 ^ 和 $re.S 使 . 匹配包括换行在内的所有字符re.U 根据Unicode字符集解析字符。这个标志影响 w, W, b, B.re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。正则表达式模式模式字符串使用特殊的语法来表示一个正则表达式:字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。多数字母和数字前加一个反斜杠时会拥有不同的含义。标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。反斜杠本身需要使用反斜杠转义。由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r't',等价于 '\t')匹配相应的特殊字符。下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。模式 描述^ 匹配字符串的开头$ 匹配字符串的末尾。. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。[...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'1 不在[]中的字符:2 匹配除了a,b,c之外的字符。re* 匹配0个或多个的表达式。re+ 匹配1个或多个的表达式。re? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式re{ n} 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的两个 o。re{ n,} 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等价于 "o+"。"o{0,}" 则等价于 "o*"。re{ n, m} 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式a| b 匹配a或b(re) 匹配括号内的表达式,也表示一个组(?imx) 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。(?-imx) 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。(?: re) 类似 (...), 但是不表示一个组(?imx: re) 在括号中使用i, m, 或 x 可选标志(?-imx: re) 在括号中不使用i, m, 或 x 可选标志(?#...) 注释.(?= re) 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。(?! re) 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功(?> re) 匹配的独立模式,省去回溯。w 匹配字母数字及下划线W 匹配非字母数字及下划线s 匹配任意空白字符,等价于 [tnrf].S 匹配任意非空字符d 匹配任意数字,等价于 [0-9].D 匹配任意非数字A 匹配字符串开始Z 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。z 匹配字符串结束G 匹配最后匹配完成的位置。b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'erb' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。B 匹配非单词边界。'erB' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。n, t, 等. 匹配一个换行符。匹配一个制表符。等1...9 匹配第n个分组的内容。10 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。正则表达式实例字符匹配实例 描述python 匹配 "python".字符类实例 描述[Pp]ython 匹配 "Python" 或 "python"rub[ye] 匹配 "ruby" 或 "rube"[aeiou] 匹配中括号内的任意一个字母[0-9] 匹配任何数字。类似于 [0123456789][a-z] 匹配任何小写字母[A-Z] 匹配任何大写字母[a-zA-Z0-9] 匹配任何字母及数字3 除了aeiou字母以外的所有字符4 匹配除了数字外的字符特殊字符类实例 描述. 匹配除 "n" 之外的任何单个字符。要匹配包括 'n' 在内的任何字符,请使用象 '[.n]' 的模式。d 匹配一个数字字符。等价于 [0-9]。D 匹配一个非数字字符。等价于 4。s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ fnrtv]。S 匹配任何非空白字符。等价于 5。w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。W 匹配任何非单词字符。等价于 '6'。 Python 面向对象 Python CGI编程 1 篇笔记 jim 264*7522@qq.com正则表达式实例: !/usr/bin/python import reline = "Cats are smarter than dogs"matchObj = re.match( r'(.) are (.?) .*', line, re.M|re.I)if matchObj: print "matchObj.group() : ", matchObj.group() print "matchObj.group(1) : ", matchObj.group(1) print "matchObj.group(2) : ", matchObj.group(2) else: print "No match!!" 正则表达式:r'(.) are (.?) .*'解析:首先,这是一个字符串,前面的一个 r 表示字符串为非转义的原始字符串,让编译器忽略反斜杠,也就是忽略转义字符。但是这个字符串里没有反斜杠,所以这个 r 可有可无。 (.) 第一个匹配分组,. 代表匹配除换行符之外的所有字符。 (.?) 第二个匹配分组,.? 后面多个问号,代表非贪婪模式,也就是说只匹配符合条件的最少字符 后面的一个 .* 没有括号包围,所以不是分组,匹配效果和第一个一样,但是不计入匹配结果中。matchObj.group() 等同于 matchObj.group(0),表示匹配到的完整文本字符matchObj.group(1) 得到第一组匹配结果,也就是(.*)匹配到的matchObj.group(2) 得到第二组匹配结果,也就是(.*?)匹配到的因为只有匹配结果中只有两组,所以如果填 3 时会报错。 ... ↩ abc ↩ aeiou ↩ 0-9 ↩ fnrtv ↩ A-Za-z0-9_ ↩

xuning715 2019-12-02 01:10:40 0 浏览量 回答数 0

回答

本文介绍了 Serverless 工作流的回调功能。相比较轮询,使用回调有效地降低了延迟、减少了轮询对服务器造成的不必要压力。另外,回调功能配合队列可以实现对非 FC 任务的编排,将 Serverless 工作流的编排范围扩展到任意类型的计算资源。 简介 长时间执行的任务通常会采用异步提交任务并返回任务标识 (ID),判断异步任务结束的方法通常有两种:轮询 (polling)和回调 (callback),在任务状态轮询中我们介绍了使用轮询来判断任务结束。Serverless 工作流的回调 (callback)功能,覆盖以下的痛点或场景: 消除轮询周期长带来的不必要延迟。 消除大流量场景下高并发的轮询造成不必要的服务器资源压力和浪费 。 编排非 FC Function 的任务,例如运行在自建机房或 ECS 上的进程 。 需要人工干预的步骤,例如通知审批通过。 下图展示了使用 MNS 队列服务集成结合回调 API 编排自建资源,拓宽了 Serverless 工作流的适用场景。 fnf-doc-service-integration-mns-queue 回调使用详解 在 Task 步骤中指定 pattern: waitForCallback,如下图状态机所示:该步骤会在提交 resourceArn 指定的任务后(如 FC invocation)该步骤会将一个 taskToken 存入到该步骤的 context 对象并进入一个暂停的状态,直到 Serverless 工作流收到回调或指定的任务超时。将 taskToken 传入 ReportTaskSucceed 或 ReportTaskFailed 接口去回调会使得该步骤继续执行。 fnf-doc-callback-state-machine - type: task name: mytask resourceArn: acs:fc:::services/{fc-service}/functions/{fc-function} pattern: waitForCallback # 指定该 Task 步骤在提交任务后等待回调。 inputMappings: - target: taskToken source: $context.task.token # 将 context 中的 taskToken 作为 input 传入 resourceArn 指定的函数。 outputMappings: - target: k source: $local.key # 将 ReportTaskSucceeded 中 output {"key": "value"} 映射成 {"k": "value"} 并作为该步骤的输出。 示例 该示例共分为以下 3 个步骤: 准备 Task Function 开始工作流 回调 步骤 1:准备 Task Function 创建下面一个简单的函数,该函数会将输入直接返回。 服务:fnf-demo。 函数:echo。 运行环境:python2.7。 函数入口:index.handler。 #!/usr/bin/env python import json def handler(event, context): return event 步骤 2:开始工作流 在 Serverless 工作流控制台创建下面的流程,并开始执行。 流程名称:fnf-demo-callback。 流程角色:配置一个有 FC Invocation 权限的角色。 version: v1 type: flow steps: - type: task name: mytask resourceArn: acs:fc:::services/fnf-demo/functions/echo pattern: waitForCallback inputMappings: - target: taskToken source: $context.task.token outputMappings: - target: s source: $local.status 流程开始后可以看到 mytask 步骤暂停在 TaskSubmitted 事件,等待回调。该事件的 output 中含有 taskToken 作为回调任务的标识。 Screen Shot 2019-08-15 at 11.00.09 AM 步骤 3:回调 使用 Serverless 工作流的 Python SDK 在本地(或其他可以运行 Python 的环境)运行 callback.py 脚本,将 {task-token} 替换为 TaskSubmitted 事件中的值。 cd /tmp mkdir fnf-demo-callback cd fnf-demo-callback 在虚拟环境中,安装 fnf python SDK。 virtualenv env source env/bin/activate pip install -t . aliyun-python-sdk-core pip install -t . aliyun-python-sdk-fnf 执行 worker 进程。 export ACCOUNT_ID={your-account-id}; export AK_ID={your-ak-id}; export AK_SECRET={your-ak-secret} python worker.py {task-token-from-TaskSubmitted} worker.py 代码: import os import sys from aliyunsdkcore.acs_exception.exceptions import ServerException from aliyunsdkcore.client import AcsClient from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest def main(): account_id = os.environ['ACCOUNT_ID'] akid = os.environ['AK_ID'] ak_secret = os.environ['AK_SECRET'] fnf_client = AcsClient(akid, ak_secret, "cn-hangzhou") task_token = sys.argv[1] print "task token " + task_token try: request = ReportTaskSucceededRequest.ReportTaskSucceededRequest() request.set_Output("{"status": "ok"}") request.set_TaskToken(task_token) resp = fnf_client.do_action_with_exception(request) print "Report task succeeded finished" except ServerException as e: print(e) if name == 'main': main() 上述脚本回调成功后可以看到 mytask 步骤继续执行, ReportTaskSucceeded 中指定的输出 "{"status": "ok"}" 经过 outputMappings 的映射后变成 "{"s": "ok"}"。

1934890530796658 2020-03-27 10:49:44 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 适用场景 表单上传是指使用OSS API中的PostObject请求来完成Object的上传,上传的Object不能超过5GB。表单上传非常适合嵌入在HTML网页中来上传Object,比较常见的场景是网站应用,以招聘网站为例: 不使用表单上传 表单上传 流程对比 网站用户上传简历。 网站服务器回应上传页面。 简历被上传到网站服务器。 网站服务器再将简历上传到OSS。 网站用户上传简历。 网站服务器回应上传页面。 简历上传到OSS。 上传限制 大小限制:使用表单上传时,Object不能超过5GB。 命名限制: 使用UTF-8编码。 长度必须在1–1023字节之间。 不能以正斜线(/)或者反斜线(\)字符开头。 优势 从流程上来说,少了一步转发,更加方便。 从架构上来说,原来的上传都统一走网站服务器,上传量过大时,需要扩容网站服务器。采用表单上传后,直接从客户端上传数据到OSS。上传量过大时,压力都在OSS上,由OSS来保障服务质量。 安全及授权 为防止第三方未经授权向开发者的Bucket上传Object,OSS提供了Bucket和Object级别的访问权限控制,详情请参见OSS细粒度的权限控制。 为了授权给第三方上传,使用的是PostPolicy方法,详情请参见PostObject。 使用表单上传的基本步骤 构建一个Post Policy。 Post请求的Policy表单域用于验证请求的合法性。例如可以指定上传的大小,可以指定上传的Object名字等,上传成功后客户端跳转到的URL,上传成功后客户端收到的状态码。具体请参考Post Policy。 例如如下Policy,网站用户能上传的过期时间是2115-01-27T10:56:19Z(这里为了测试成功写的过期时间很长,实际使用中不建议这样设置),能上传的文件最大为104857600字节。 以Python代码为例子,Policy是json格式的字符串。 policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 104857600]]}" 将Policy字符串进行base64编码。 用OSS的AccessKeySecret对base64编码后的Policy进行签名。 构建上传的HTML页面。 打开HTML页面,选择文件上传。 完整Python代码示例: #coding=utf8 import md5 import hashlib import base64 import hmac from optparse import OptionParser def convert_base64(input): return base64.b64encode(input) def get_sign_policy(key, policy): return base64.b64encode(hmac.new(key, policy, hashlib.sha1).digest()) def get_form(bucket, endpoint, access_key_id, access_key_secret, out): #1 构建一个Post Policy policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 1048576]]}" print("policy: %s" % policy) #2 将Policy字符串进行base64编码 base64policy = convert_base64(policy) print("base64_encode_policy: %s" % base64policy) #3 用OSS的AccessKeySecret对编码后的Policy进行签名 signature = get_sign_policy(access_key_secret, base64policy) #4 构建上传的HTML页面 form = ''' <html> <meta http-equiv=content-type content="text/html; charset=UTF-8"> <head><title>OSS表单上传(PostObject)</title></head> <body> <form action="http://%s.%s" method="post" enctype="multipart/form-data"> <input type="text" name="OSSAccessKeyId" value="%s"> <input type="text" name="policy" value="%s"> <input type="text" name="Signature" value="%s"> <input type="text" name="key" value="upload/${filename}"> <input type="text" name="success_action_redirect" value="http://oss.aliyun.com"> <input type="text" name="success_action_status" value="201"> <input name="file" type="file" id="file"> <input name="submit" value="Upload" type="submit"> </form> </body> </html> ''' % (bucket, endpoint, access_key_id, base64policy, signature) f = open(out, "wb") f.write(form) f.close() print("form is saved into %s" % out) if __name__ == '__main__': parser = OptionParser() parser.add_option("", "--bucket", dest="bucket", help="specify ") parser.add_option("", "--endpoint", dest="endpoint", help="specify") parser.add_option("", "--id", dest="id", help="access_key_id") parser.add_option("", "--key", dest="key", help="access_key_secret") parser.add_option("", "--out", dest="out", help="out put form") (opts, args) = parser.parse_args() if opts.bucket and opts.endpoint and opts.id and opts.key and opts.out: get_form(opts.bucket, opts.endpoint, opts.id, opts.key, opts.out) else: print "python %s --bucket=your-bucket --endpoint=oss-cn-hangzhou.aliyuncs.com --id=your-access-key-id --key=your-access-key-secret --out=out-put-form-name" % __file__ 将此段代码保存为post_object.py,然后用python post_object.py来运行。 用法: python post_object.py --bucket=您的Bucket --endpoint=Bucket对应的OSS域名 --id=您的AccessKeyId --key=您的AccessKeySecret --out=输出的文件名 示例: python post_object.py --bucket=oss-sample --endpoint=oss-cn-hangzhou.aliyuncs.com --id=tphpxp --key=ZQNJzf4QJRkrH4 --out=post.html 说明 构建的表单中success_action_redirect value=http://oss.aliyun.com 表示上传成功后跳转的页面。可以替换成您自己的页面。 构建的表单中success_action_status value=201表示的是上传成功后返回的状态码为201。可以替换。 假如指定生成的HTML文件为post.html,打开post.html,选择文件上传。在这个例子中如果成功则会跳转到OSS的主页面。 功能使用参考 API:PostObject Java SDK:表单上传 最佳实践 Web端直传实践 跨域资源共享(CORS)

2019-12-01 23:12:29 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 适用场景 表单上传是指使用OSS API中的PostObject请求来完成Object的上传,上传的Object不能超过5GB。表单上传非常适合嵌入在HTML网页中来上传Object,比较常见的场景是网站应用,以招聘网站为例: 不使用表单上传 表单上传 流程对比 网站用户上传简历。 网站服务器回应上传页面。 简历被上传到网站服务器。 网站服务器再将简历上传到OSS。 网站用户上传简历。 网站服务器回应上传页面。 简历上传到OSS。 上传限制 大小限制:使用表单上传时,Object不能超过5GB。 命名限制: 使用UTF-8编码。 长度必须在1–1023字节之间。 不能以正斜线(/)或者反斜线(\)字符开头。 优势 从流程上来说,少了一步转发,更加方便。 从架构上来说,原来的上传都统一走网站服务器,上传量过大时,需要扩容网站服务器。采用表单上传后,直接从客户端上传数据到OSS。上传量过大时,压力都在OSS上,由OSS来保障服务质量。 安全及授权 为防止第三方未经授权向开发者的Bucket上传Object,OSS提供了Bucket和Object级别的访问权限控制,详情请参见OSS细粒度的权限控制。 为了授权给第三方上传,使用的是PostPolicy方法,详情请参见PostObject。 使用表单上传的基本步骤 构建一个Post Policy。 Post请求的Policy表单域用于验证请求的合法性。例如可以指定上传的大小,可以指定上传的Object名字等,上传成功后客户端跳转到的URL,上传成功后客户端收到的状态码。具体请参考Post Policy。 例如如下Policy,网站用户能上传的过期时间是2115-01-27T10:56:19Z(这里为了测试成功写的过期时间很长,实际使用中不建议这样设置),能上传的文件最大为104857600字节。 以Python代码为例子,Policy是json格式的字符串。 policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 104857600]]}" 将Policy字符串进行base64编码。 用OSS的AccessKeySecret对base64编码后的Policy进行签名。 构建上传的HTML页面。 打开HTML页面,选择文件上传。 完整Python代码示例: #coding=utf8 import md5 import hashlib import base64 import hmac from optparse import OptionParser def convert_base64(input): return base64.b64encode(input) def get_sign_policy(key, policy): return base64.b64encode(hmac.new(key, policy, hashlib.sha1).digest()) def get_form(bucket, endpoint, access_key_id, access_key_secret, out): #1 构建一个Post Policy policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 1048576]]}" print("policy: %s" % policy) #2 将Policy字符串进行base64编码 base64policy = convert_base64(policy) print("base64_encode_policy: %s" % base64policy) #3 用OSS的AccessKeySecret对编码后的Policy进行签名 signature = get_sign_policy(access_key_secret, base64policy) #4 构建上传的HTML页面 form = ''' <html> <meta http-equiv=content-type content="text/html; charset=UTF-8"> <head><title>OSS表单上传(PostObject)</title></head> <body> <form action="http://%s.%s" method="post" enctype="multipart/form-data"> <input type="text" name="OSSAccessKeyId" value="%s"> <input type="text" name="policy" value="%s"> <input type="text" name="Signature" value="%s"> <input type="text" name="key" value="upload/${filename}"> <input type="text" name="success_action_redirect" value="http://oss.aliyun.com"> <input type="text" name="success_action_status" value="201"> <input name="file" type="file" id="file"> <input name="submit" value="Upload" type="submit"> </form> </body> </html> ''' % (bucket, endpoint, access_key_id, base64policy, signature) f = open(out, "wb") f.write(form) f.close() print("form is saved into %s" % out) if __name__ == '__main__': parser = OptionParser() parser.add_option("", "--bucket", dest="bucket", help="specify ") parser.add_option("", "--endpoint", dest="endpoint", help="specify") parser.add_option("", "--id", dest="id", help="access_key_id") parser.add_option("", "--key", dest="key", help="access_key_secret") parser.add_option("", "--out", dest="out", help="out put form") (opts, args) = parser.parse_args() if opts.bucket and opts.endpoint and opts.id and opts.key and opts.out: get_form(opts.bucket, opts.endpoint, opts.id, opts.key, opts.out) else: print "python %s --bucket=your-bucket --endpoint=oss-cn-hangzhou.aliyuncs.com --id=your-access-key-id --key=your-access-key-secret --out=out-put-form-name" % __file__ 将此段代码保存为post_object.py,然后用python post_object.py来运行。 用法: python post_object.py --bucket=您的Bucket --endpoint=Bucket对应的OSS域名 --id=您的AccessKeyId --key=您的AccessKeySecret --out=输出的文件名 示例: python post_object.py --bucket=oss-sample --endpoint=oss-cn-hangzhou.aliyuncs.com --id=tphpxp --key=ZQNJzf4QJRkrH4 --out=post.html 说明 构建的表单中success_action_redirect value=http://oss.aliyun.com 表示上传成功后跳转的页面。可以替换成您自己的页面。 构建的表单中success_action_status value=201表示的是上传成功后返回的状态码为201。可以替换。 假如指定生成的HTML文件为post.html,打开post.html,选择文件上传。在这个例子中如果成功则会跳转到OSS的主页面。 功能使用参考 API:PostObject Java SDK:表单上传 最佳实践 Web端直传实践 跨域资源共享(CORS)

2019-12-01 23:12:30 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 适用场景 表单上传是指使用OSS API中的PostObject请求来完成Object的上传,上传的Object不能超过5GB。表单上传非常适合嵌入在HTML网页中来上传Object,比较常见的场景是网站应用,以招聘网站为例: 不使用表单上传 表单上传 流程对比 网站用户上传简历。 网站服务器回应上传页面。 简历被上传到网站服务器。 网站服务器再将简历上传到OSS。 网站用户上传简历。 网站服务器回应上传页面。 简历上传到OSS。 上传限制 大小限制:使用表单上传时,Object不能超过5GB。 命名限制: 使用UTF-8编码。 长度必须在1–1023字节之间。 不能以正斜线(/)或者反斜线(\)字符开头。 优势 从流程上来说,少了一步转发,更加方便。 从架构上来说,原来的上传都统一走网站服务器,上传量过大时,需要扩容网站服务器。采用表单上传后,直接从客户端上传数据到OSS。上传量过大时,压力都在OSS上,由OSS来保障服务质量。 安全及授权 为防止第三方未经授权向开发者的Bucket上传Object,OSS提供了Bucket和Object级别的访问权限控制,详情请参见OSS细粒度的权限控制。 为了授权给第三方上传,使用的是PostPolicy方法,详情请参见PostObject。 使用表单上传的基本步骤 构建一个Post Policy。 Post请求的Policy表单域用于验证请求的合法性。例如可以指定上传的大小,可以指定上传的Object名字等,上传成功后客户端跳转到的URL,上传成功后客户端收到的状态码。具体请参考Post Policy。 例如如下Policy,网站用户能上传的过期时间是2115-01-27T10:56:19Z(这里为了测试成功写的过期时间很长,实际使用中不建议这样设置),能上传的文件最大为104857600字节。 以Python代码为例子,Policy是json格式的字符串。 policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 104857600]]}" 将Policy字符串进行base64编码。 用OSS的AccessKeySecret对base64编码后的Policy进行签名。 构建上传的HTML页面。 打开HTML页面,选择文件上传。 完整Python代码示例: #coding=utf8 import md5 import hashlib import base64 import hmac from optparse import OptionParser def convert_base64(input): return base64.b64encode(input) def get_sign_policy(key, policy): return base64.b64encode(hmac.new(key, policy, hashlib.sha1).digest()) def get_form(bucket, endpoint, access_key_id, access_key_secret, out): #1 构建一个Post Policy policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 1048576]]}" print("policy: %s" % policy) #2 将Policy字符串进行base64编码 base64policy = convert_base64(policy) print("base64_encode_policy: %s" % base64policy) #3 用OSS的AccessKeySecret对编码后的Policy进行签名 signature = get_sign_policy(access_key_secret, base64policy) #4 构建上传的HTML页面 form = ''' <html> <meta http-equiv=content-type content="text/html; charset=UTF-8"> <head><title>OSS表单上传(PostObject)</title></head> <body> <form action="http://%s.%s" method="post" enctype="multipart/form-data"> <input type="text" name="OSSAccessKeyId" value="%s"> <input type="text" name="policy" value="%s"> <input type="text" name="Signature" value="%s"> <input type="text" name="key" value="upload/${filename}"> <input type="text" name="success_action_redirect" value="http://oss.aliyun.com"> <input type="text" name="success_action_status" value="201"> <input name="file" type="file" id="file"> <input name="submit" value="Upload" type="submit"> </form> </body> </html> ''' % (bucket, endpoint, access_key_id, base64policy, signature) f = open(out, "wb") f.write(form) f.close() print("form is saved into %s" % out) if __name__ == '__main__': parser = OptionParser() parser.add_option("", "--bucket", dest="bucket", help="specify ") parser.add_option("", "--endpoint", dest="endpoint", help="specify") parser.add_option("", "--id", dest="id", help="access_key_id") parser.add_option("", "--key", dest="key", help="access_key_secret") parser.add_option("", "--out", dest="out", help="out put form") (opts, args) = parser.parse_args() if opts.bucket and opts.endpoint and opts.id and opts.key and opts.out: get_form(opts.bucket, opts.endpoint, opts.id, opts.key, opts.out) else: print "python %s --bucket=your-bucket --endpoint=oss-cn-hangzhou.aliyuncs.com --id=your-access-key-id --key=your-access-key-secret --out=out-put-form-name" % __file__ 将此段代码保存为post_object.py,然后用python post_object.py来运行。 用法: python post_object.py --bucket=您的Bucket --endpoint=Bucket对应的OSS域名 --id=您的AccessKeyId --key=您的AccessKeySecret --out=输出的文件名 示例: python post_object.py --bucket=oss-sample --endpoint=oss-cn-hangzhou.aliyuncs.com --id=tphpxp --key=ZQNJzf4QJRkrH4 --out=post.html 说明 构建的表单中success_action_redirect value=http://oss.aliyun.com 表示上传成功后跳转的页面。可以替换成您自己的页面。 构建的表单中success_action_status value=201表示的是上传成功后返回的状态码为201。可以替换。 假如指定生成的HTML文件为post.html,打开post.html,选择文件上传。在这个例子中如果成功则会跳转到OSS的主页面。 功能使用参考 API:PostObject Java SDK:表单上传 最佳实践 Web端直传实践 跨域资源共享(CORS)

2019-12-01 23:12:30 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 适用场景 表单上传是指使用OSS API中的PostObject请求来完成Object的上传,上传的Object不能超过5GB。表单上传非常适合嵌入在HTML网页中来上传Object,比较常见的场景是网站应用,以招聘网站为例: 不使用表单上传 表单上传 流程对比 网站用户上传简历。 网站服务器回应上传页面。 简历被上传到网站服务器。 网站服务器再将简历上传到OSS。 网站用户上传简历。 网站服务器回应上传页面。 简历上传到OSS。 上传限制 大小限制:使用表单上传时,Object不能超过5GB。 命名限制: 使用UTF-8编码。 长度必须在1–1023字节之间。 不能以正斜线(/)或者反斜线(\)字符开头。 优势 从流程上来说,少了一步转发,更加方便。 从架构上来说,原来的上传都统一走网站服务器,上传量过大时,需要扩容网站服务器。采用表单上传后,直接从客户端上传数据到OSS。上传量过大时,压力都在OSS上,由OSS来保障服务质量。 安全及授权 为防止第三方未经授权向开发者的Bucket上传Object,OSS提供了Bucket和Object级别的访问权限控制,详情请参见OSS细粒度的权限控制。 为了授权给第三方上传,使用的是PostPolicy方法,详情请参见PostObject。 使用表单上传的基本步骤 构建一个Post Policy。 Post请求的Policy表单域用于验证请求的合法性。例如可以指定上传的大小,可以指定上传的Object名字等,上传成功后客户端跳转到的URL,上传成功后客户端收到的状态码。具体请参考Post Policy。 例如如下Policy,网站用户能上传的过期时间是2115-01-27T10:56:19Z(这里为了测试成功写的过期时间很长,实际使用中不建议这样设置),能上传的文件最大为104857600字节。 以Python代码为例子,Policy是json格式的字符串。 policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 104857600]]}" 将Policy字符串进行base64编码。 用OSS的AccessKeySecret对base64编码后的Policy进行签名。 构建上传的HTML页面。 打开HTML页面,选择文件上传。 完整Python代码示例: #coding=utf8 import md5 import hashlib import base64 import hmac from optparse import OptionParser def convert_base64(input): return base64.b64encode(input) def get_sign_policy(key, policy): return base64.b64encode(hmac.new(key, policy, hashlib.sha1).digest()) def get_form(bucket, endpoint, access_key_id, access_key_secret, out): #1 构建一个Post Policy policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 1048576]]}" print("policy: %s" % policy) #2 将Policy字符串进行base64编码 base64policy = convert_base64(policy) print("base64_encode_policy: %s" % base64policy) #3 用OSS的AccessKeySecret对编码后的Policy进行签名 signature = get_sign_policy(access_key_secret, base64policy) #4 构建上传的HTML页面 form = ''' <html> <meta http-equiv=content-type content="text/html; charset=UTF-8"> <head><title>OSS表单上传(PostObject)</title></head> <body> <form action="http://%s.%s" method="post" enctype="multipart/form-data"> <input type="text" name="OSSAccessKeyId" value="%s"> <input type="text" name="policy" value="%s"> <input type="text" name="Signature" value="%s"> <input type="text" name="key" value="upload/${filename}"> <input type="text" name="success_action_redirect" value="http://oss.aliyun.com"> <input type="text" name="success_action_status" value="201"> <input name="file" type="file" id="file"> <input name="submit" value="Upload" type="submit"> </form> </body> </html> ''' % (bucket, endpoint, access_key_id, base64policy, signature) f = open(out, "wb") f.write(form) f.close() print("form is saved into %s" % out) if __name__ == '__main__': parser = OptionParser() parser.add_option("", "--bucket", dest="bucket", help="specify ") parser.add_option("", "--endpoint", dest="endpoint", help="specify") parser.add_option("", "--id", dest="id", help="access_key_id") parser.add_option("", "--key", dest="key", help="access_key_secret") parser.add_option("", "--out", dest="out", help="out put form") (opts, args) = parser.parse_args() if opts.bucket and opts.endpoint and opts.id and opts.key and opts.out: get_form(opts.bucket, opts.endpoint, opts.id, opts.key, opts.out) else: print "python %s --bucket=your-bucket --endpoint=oss-cn-hangzhou.aliyuncs.com --id=your-access-key-id --key=your-access-key-secret --out=out-put-form-name" % __file__ 将此段代码保存为post_object.py,然后用python post_object.py来运行。 用法: python post_object.py --bucket=您的Bucket --endpoint=Bucket对应的OSS域名 --id=您的AccessKeyId --key=您的AccessKeySecret --out=输出的文件名 示例: python post_object.py --bucket=oss-sample --endpoint=oss-cn-hangzhou.aliyuncs.com --id=tphpxp --key=ZQNJzf4QJRkrH4 --out=post.html 说明 构建的表单中success_action_redirect value=http://oss.aliyun.com 表示上传成功后跳转的页面。可以替换成您自己的页面。 构建的表单中success_action_status value=201表示的是上传成功后返回的状态码为201。可以替换。 假如指定生成的HTML文件为post.html,打开post.html,选择文件上传。在这个例子中如果成功则会跳转到OSS的主页面。 功能使用参考 API:PostObject Java SDK:表单上传 最佳实践 Web端直传实践 跨域资源共享(CORS)

2019-12-01 23:12:29 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 适用场景 表单上传是指使用OSS API中的PostObject请求来完成Object的上传,上传的Object不能超过5GB。表单上传非常适合嵌入在HTML网页中来上传Object,比较常见的场景是网站应用,以招聘网站为例: 不使用表单上传 表单上传 流程对比 网站用户上传简历。 网站服务器回应上传页面。 简历被上传到网站服务器。 网站服务器再将简历上传到OSS。 网站用户上传简历。 网站服务器回应上传页面。 简历上传到OSS。 上传限制 大小限制:使用表单上传时,Object不能超过5GB。 命名限制: 使用UTF-8编码。 长度必须在1–1023字节之间。 不能以正斜线(/)或者反斜线(\)字符开头。 优势 从流程上来说,少了一步转发,更加方便。 从架构上来说,原来的上传都统一走网站服务器,上传量过大时,需要扩容网站服务器。采用表单上传后,直接从客户端上传数据到OSS。上传量过大时,压力都在OSS上,由OSS来保障服务质量。 安全及授权 为防止第三方未经授权向开发者的Bucket上传Object,OSS提供了Bucket和Object级别的访问权限控制,详情请参见OSS细粒度的权限控制。 为了授权给第三方上传,使用的是PostPolicy方法,详情请参见PostObject。 使用表单上传的基本步骤 构建一个Post Policy。 Post请求的Policy表单域用于验证请求的合法性。例如可以指定上传的大小,可以指定上传的Object名字等,上传成功后客户端跳转到的URL,上传成功后客户端收到的状态码。具体请参考Post Policy。 例如如下Policy,网站用户能上传的过期时间是2115-01-27T10:56:19Z(这里为了测试成功写的过期时间很长,实际使用中不建议这样设置),能上传的文件最大为104857600字节。 以Python代码为例子,Policy是json格式的字符串。 policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 104857600]]}" 将Policy字符串进行base64编码。 用OSS的AccessKeySecret对base64编码后的Policy进行签名。 构建上传的HTML页面。 打开HTML页面,选择文件上传。 完整Python代码示例: #coding=utf8 import md5 import hashlib import base64 import hmac from optparse import OptionParser def convert_base64(input): return base64.b64encode(input) def get_sign_policy(key, policy): return base64.b64encode(hmac.new(key, policy, hashlib.sha1).digest()) def get_form(bucket, endpoint, access_key_id, access_key_secret, out): #1 构建一个Post Policy policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 1048576]]}" print("policy: %s" % policy) #2 将Policy字符串进行base64编码 base64policy = convert_base64(policy) print("base64_encode_policy: %s" % base64policy) #3 用OSS的AccessKeySecret对编码后的Policy进行签名 signature = get_sign_policy(access_key_secret, base64policy) #4 构建上传的HTML页面 form = ''' <html> <meta http-equiv=content-type content="text/html; charset=UTF-8"> <head><title>OSS表单上传(PostObject)</title></head> <body> <form action="http://%s.%s" method="post" enctype="multipart/form-data"> <input type="text" name="OSSAccessKeyId" value="%s"> <input type="text" name="policy" value="%s"> <input type="text" name="Signature" value="%s"> <input type="text" name="key" value="upload/${filename}"> <input type="text" name="success_action_redirect" value="http://oss.aliyun.com"> <input type="text" name="success_action_status" value="201"> <input name="file" type="file" id="file"> <input name="submit" value="Upload" type="submit"> </form> </body> </html> ''' % (bucket, endpoint, access_key_id, base64policy, signature) f = open(out, "wb") f.write(form) f.close() print("form is saved into %s" % out) if __name__ == '__main__': parser = OptionParser() parser.add_option("", "--bucket", dest="bucket", help="specify ") parser.add_option("", "--endpoint", dest="endpoint", help="specify") parser.add_option("", "--id", dest="id", help="access_key_id") parser.add_option("", "--key", dest="key", help="access_key_secret") parser.add_option("", "--out", dest="out", help="out put form") (opts, args) = parser.parse_args() if opts.bucket and opts.endpoint and opts.id and opts.key and opts.out: get_form(opts.bucket, opts.endpoint, opts.id, opts.key, opts.out) else: print "python %s --bucket=your-bucket --endpoint=oss-cn-hangzhou.aliyuncs.com --id=your-access-key-id --key=your-access-key-secret --out=out-put-form-name" % __file__ 将此段代码保存为post_object.py,然后用python post_object.py来运行。 用法: python post_object.py --bucket=您的Bucket --endpoint=Bucket对应的OSS域名 --id=您的AccessKeyId --key=您的AccessKeySecret --out=输出的文件名 示例: python post_object.py --bucket=oss-sample --endpoint=oss-cn-hangzhou.aliyuncs.com --id=tphpxp --key=ZQNJzf4QJRkrH4 --out=post.html 说明 构建的表单中success_action_redirect value=http://oss.aliyun.com 表示上传成功后跳转的页面。可以替换成您自己的页面。 构建的表单中success_action_status value=201表示的是上传成功后返回的状态码为201。可以替换。 假如指定生成的HTML文件为post.html,打开post.html,选择文件上传。在这个例子中如果成功则会跳转到OSS的主页面。 功能使用参考 API:PostObject Java SDK:表单上传 最佳实践 Web端直传实践 跨域资源共享(CORS)

2019-12-01 23:12:30 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 适用场景 表单上传是指使用OSS API中的PostObject请求来完成Object的上传,上传的Object不能超过5GB。表单上传非常适合嵌入在HTML网页中来上传Object,比较常见的场景是网站应用,以招聘网站为例: 不使用表单上传 表单上传 流程对比 网站用户上传简历。 网站服务器回应上传页面。 简历被上传到网站服务器。 网站服务器再将简历上传到OSS。 网站用户上传简历。 网站服务器回应上传页面。 简历上传到OSS。 上传限制 大小限制:使用表单上传时,Object不能超过5GB。 命名限制: 使用UTF-8编码。 长度必须在1–1023字节之间。 不能以正斜线(/)或者反斜线(\)字符开头。 优势 从流程上来说,少了一步转发,更加方便。 从架构上来说,原来的上传都统一走网站服务器,上传量过大时,需要扩容网站服务器。采用表单上传后,直接从客户端上传数据到OSS。上传量过大时,压力都在OSS上,由OSS来保障服务质量。 安全及授权 为防止第三方未经授权向开发者的Bucket上传Object,OSS提供了Bucket和Object级别的访问权限控制,详情请参见OSS细粒度的权限控制。 为了授权给第三方上传,使用的是PostPolicy方法,详情请参见PostObject。 使用表单上传的基本步骤 构建一个Post Policy。 Post请求的Policy表单域用于验证请求的合法性。例如可以指定上传的大小,可以指定上传的Object名字等,上传成功后客户端跳转到的URL,上传成功后客户端收到的状态码。具体请参考Post Policy。 例如如下Policy,网站用户能上传的过期时间是2115-01-27T10:56:19Z(这里为了测试成功写的过期时间很长,实际使用中不建议这样设置),能上传的文件最大为104857600字节。 以Python代码为例子,Policy是json格式的字符串。 policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 104857600]]}" 将Policy字符串进行base64编码。 用OSS的AccessKeySecret对base64编码后的Policy进行签名。 构建上传的HTML页面。 打开HTML页面,选择文件上传。 完整Python代码示例: #coding=utf8 import md5 import hashlib import base64 import hmac from optparse import OptionParser def convert_base64(input): return base64.b64encode(input) def get_sign_policy(key, policy): return base64.b64encode(hmac.new(key, policy, hashlib.sha1).digest()) def get_form(bucket, endpoint, access_key_id, access_key_secret, out): #1 构建一个Post Policy policy="{\"expiration\":\"2115-01-27T10:56:19Z\",\"conditions\":[[\"content-length-range\", 0, 1048576]]}" print("policy: %s" % policy) #2 将Policy字符串进行base64编码 base64policy = convert_base64(policy) print("base64_encode_policy: %s" % base64policy) #3 用OSS的AccessKeySecret对编码后的Policy进行签名 signature = get_sign_policy(access_key_secret, base64policy) #4 构建上传的HTML页面 form = ''' <html> <meta http-equiv=content-type content="text/html; charset=UTF-8"> <head><title>OSS表单上传(PostObject)</title></head> <body> <form action="http://%s.%s" method="post" enctype="multipart/form-data"> <input type="text" name="OSSAccessKeyId" value="%s"> <input type="text" name="policy" value="%s"> <input type="text" name="Signature" value="%s"> <input type="text" name="key" value="upload/${filename}"> <input type="text" name="success_action_redirect" value="http://oss.aliyun.com"> <input type="text" name="success_action_status" value="201"> <input name="file" type="file" id="file"> <input name="submit" value="Upload" type="submit"> </form> </body> </html> ''' % (bucket, endpoint, access_key_id, base64policy, signature) f = open(out, "wb") f.write(form) f.close() print("form is saved into %s" % out) if __name__ == '__main__': parser = OptionParser() parser.add_option("", "--bucket", dest="bucket", help="specify ") parser.add_option("", "--endpoint", dest="endpoint", help="specify") parser.add_option("", "--id", dest="id", help="access_key_id") parser.add_option("", "--key", dest="key", help="access_key_secret") parser.add_option("", "--out", dest="out", help="out put form") (opts, args) = parser.parse_args() if opts.bucket and opts.endpoint and opts.id and opts.key and opts.out: get_form(opts.bucket, opts.endpoint, opts.id, opts.key, opts.out) else: print "python %s --bucket=your-bucket --endpoint=oss-cn-hangzhou.aliyuncs.com --id=your-access-key-id --key=your-access-key-secret --out=out-put-form-name" % __file__ 将此段代码保存为post_object.py,然后用python post_object.py来运行。 用法: python post_object.py --bucket=您的Bucket --endpoint=Bucket对应的OSS域名 --id=您的AccessKeyId --key=您的AccessKeySecret --out=输出的文件名 示例: python post_object.py --bucket=oss-sample --endpoint=oss-cn-hangzhou.aliyuncs.com --id=tphpxp --key=ZQNJzf4QJRkrH4 --out=post.html 说明 构建的表单中success_action_redirect value=http://oss.aliyun.com 表示上传成功后跳转的页面。可以替换成您自己的页面。 构建的表单中success_action_status value=201表示的是上传成功后返回的状态码为201。可以替换。 假如指定生成的HTML文件为post.html,打开post.html,选择文件上传。在这个例子中如果成功则会跳转到OSS的主页面。 功能使用参考 API:PostObject Java SDK:表单上传 最佳实践 Web端直传实践 跨域资源共享(CORS)

2019-12-01 23:12:29 0 浏览量 回答数 0

回答

本文以部署在ECS实例上的Python访问OSS为例,详细介绍了如何借助ECS实例RAM角色,使实例内部的应用程序可以使用STS临时凭证访问其他云产品。 前提条件 已注册阿里云账号。如还未注册,请先完成账号注册。 已创建一个ECS实例,详情请参见使用向导创建实例。 背景信息 以往部署在ECS实例中的应用程序如果需要访问阿里云其他云产品,您通常需要借助AccessKeyID和AccessKeySecret(下文简称AK)来实现。AK是您访问阿里云API的密钥,具有相应账号的完整权限。为了方便应用程序对AK的管理,您通常需要将AK保存在应用程序的配置文件中或以其他方式保存在ECS实例中,这在一定程度上增加了AK管理的复杂性,并且降低了AK的保密性。甚至,如果您需要实现多地域一致性部署,AK会随着镜像以及使用镜像创建的实例扩散出去。这种情况下,当您需要更换AK时,您就需要逐台更新和重新部署实例和镜像。 现在借助于ECS实例RAM角色,您可以将RAM角色和ECS实例关联起来,实例内部的应用程序可以通过STS临时凭证访问其他云产品。其中STS临时凭证由系统自动生成和更新,应用程序可以使用指定的实例元数据URL获取STS临时凭证,无需特别管理。同时借助于RAM,通过对角色和授权策略的管理,您可以达到不同实例对不同云产品或相同云产品具有各自访问权限的目的。 说明 为了方便您随本文样例快速入门,文档里所有操作均在OpenAPI Explorer完成。OpenAPI Explorer通过已登录用户信息获取当前账号临时AK,对当前账号发起线上资源操作,请谨慎操作。创建实例操作会产生费用。操作完成后请及时释放实例。 操作步骤 为了使ECS借助实例RAM角色,实现内部Python可以使用STS临时凭证访问OSS,您需要完成以下步骤: 步骤一:创建RAM角色并配置授权策略 步骤二:指定RAM角色创建并设置ECS实例 步骤三:在实例内部访问实例元数据URL获取STS临时凭证 步骤四:基于临时凭证,使用Python SDK访问OSS 步骤一:创建RAM角色并配置授权策略 完成以下步骤,创建RAM角色并配置授权策略: 创建RAM角色。 找到OpenAPI Explorer RAM产品下CreateRole API。其中: RoleName:设置角色的名称。根据自己的需要填写,本示例中为 EcsRamRoleTest。 AssumeRolePolicyDocument: 填写如下内容,表示该角色为一个服务角色,受信云服务(本示例中为ECS)可以扮演该角色。 { "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "Service": [ "ecs.aliyuncs.com" ] } } ], "Version": "1" } 创建授权策略。 找到OpenAPI Explorer RAM产品下的CreatePolicy API。其中: PolicyName:设置授权策略的名称。本示例中为EcsRamRolePolicyTest。 PolicyDocument:输入授权策略内容。本示例中填写如下内容,表示该角色具有OSS只读权限。 { "Statement": [ { "Action": [ "oss:Get*", "oss:List*" ], "Effect": "Allow", "Resource": "*" } ], "Version": "1" } 为角色附加授权。找到OpenAPI Explorer RAM产品下的AttachPolicyToRole API。 PolicyType:填写Custom。 PolicyName:填写第2步创建的策略名称,如本示例中的EcsRamRolePolicyTest。 RoleName:填写第1步创建的角色名称,如本示例中的EcsRamRoleTest。 步骤二:指定RAM角色创建并设置ECS实例 您可以通过以下任一种方式为ECS实例指定RAM角色: 将实例RAM角色附加到一个已创建的ECS实例上 您可以使用ECS的AttachInstanceRamRole API附加实例RAM角色到已有的VPC类型ECS实例授权访问,设置信息如下: RegionId:为实例所在的地域ID。 RamRoleName:RAM角色的名称。本示例中为EcsRamRoleTest。 InstanceIds:需要附加实例RAM角色的VPC类型ECS实例ID。本示例中为["i-bXXXXXXXX"]。 指定RAM角色创建并设置ECS实例 按以下步骤指定RAM角色创建并设置ECS实例。 创建实例。 找到OpenAPI Explorer ECS产品下的CreateInstance API,根据实际情况填写请求参数。必须填写的参数包括: RegionId:实例所在地域。本示例中为cn-hangzhou。 ImageId:实例的镜像。本示例中为centos_7_03_64_40G_alibase_20170503.vhd。 InstanceType:实例的规格。本示例中为ecs.g6.large。 VSwitchId:实例所在的VPC虚拟交换机。因为ECS实例RAM角色目前只支持VPC类型ECS实例,所以VSwitchId是必需的。 RamRoleName:RAM角色的名称。本示例中为EcsRamRoleTest。 如果您希望授权子账号创建指定RAM角色的ECS实例,那么子账号除了拥有创建ECS实例的权限之外,还需要增加PassRole权限。所以,您需要创建一个如下所示的自定义授权策略并绑定到子账号上。 如果是创建ECS实例,[ECS RAM Action]可以是ecs:CreateInstance,您也可以根据实际情况添加更多的权限。 如果您需要为子账号授予所有ECS操作权限,[ECS RAM Action]应该替换为ecs:*。 说明 [ECS RAM Action]的取值详情请参见鉴权规则。 { "Statement": [ { "Action": "[ECS RAM Action]", "Resource": "*", "Effect": "Allow" }, { "Action": "ram:PassRole", "Resource": "*", "Effect": "Allow" } ], "Version": "1" } 设置密码并启动实例。 使用API或在控制台设置ECS实例能访问公网。 步骤三:在实例内部访问实例元数据URL获取STS临时凭证 完成以下步骤,获取实例的STS临时凭证: 说明 STS临时凭证失效前半小时会生成新的STS临时凭证,在这半小时内,新旧STS临时凭证均可使用。 远程连接ECS实例。连接方式请参见连接方式概述。 访问http://100.100.100.200/latest/meta-data/ram/security-credentials/EcsRamRoleTest获取STS临时凭证。路径最后一部分是RAM角色名称,您应替换为自己的创建的RAM角色名称。 说明 本示例中使用curl命令访问上述URL。如果您使用的是Windows ECS实例,请参见实例元数据。 示例输出结果如下。 [root@local ~]# curl http://100.100.100.200/latest/meta-data/ram/security-credentials/EcsRamRoleTest { "AccessKeyId" : "STS.J8XXXXXXXXXX4", "AccessKeySecret" : "9PjfXXXXXXXXXBf2XAW", "Expiration" : "2017-06-09T09:17:19Z", "SecurityToken" : "CAIXXXXXXXXXXXwmBkleCTkyI+", "LastUpdated" : "2017-06-09T03:17:18Z", "Code" : "Success" } 步骤四:基于临时凭证,使用Python SDK访问OSS 本示例中,我们基于STS临时凭证使用Python SDK列举实例所在地域的某个OSS存储空间(Bucket)里的10个文件。 前提条件: 您已经远程连接到ECS实例。 您的ECS实例已经安装了Python。如果您用的是Linux ECS实例,必须安装pip。 您在实例所在的地域已经创建了存储空间(Bucket),并已经获取Bucket的名称和Endpoint。本示例中,Bucket名称为ramroletest,Endpoint为oss-cn-hangzhou.aliyuncs.com。 完成以下步骤,使用Python SDK访问OSS: 运行命令pip install oss2,安装OSS Python SDK。 说明 如果您用的是Windows ECS实例,请参见 《对象存储 OSS SDK 参考》 的安装 Python SDK。 执行下述命令进行测试。 import oss2 from itertools import islice auth = oss2.StsAuth(<AccessKeyId>, <AccessKeySecret>, <SecurityToken>) bucket = oss2.Bucket(auth, <您的 Endpoint>, <您的 Bucket 名称>) for b in islice(oss2.ObjectIterator(bucket), 10): print(b.key) 其中: oss2.StsAuth中的3个参数分别对应于步骤三中返回的AccessKeyId、AccessKeySecret和SecurityToken。 oss2.Bucket中后2个参数是Bucket的名称和Endpoint。 示例输出结果如下。 [root@local ~]# python Python 2.7.5 (default, Nov 6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import oss2 >>> from itertools import islice >>> auth = oss2.StsAuth("STS.J8XXXXXXXXXX4", "9PjfXXXXXXXXXBf2XAW", "CAIXXXXXXXXXXXwmBkleCTkyI+") >>> bucket = oss2.Bucket(auth, "oss-cn-hangzhou.aliyuncs.com", "ramroletest") >>> for b in islice(oss2.ObjectIterator(bucket), 10): ... print(b.key) ... ramroletest.txt test.shh

1934890530796658 2020-03-25 18:42:34 0 浏览量 回答数 0

回答

文本清理问题会涉及到包括文本解析与数据处理等一系列问题。 在非常简单的情形下,你可能会选择使用字符串函数(比如 str.upper() 和 str.lower() )将文本转为标准格式。 使用 str.replace() 或者 re.sub() 的简单替换操作能删除或者改变指定的字符序列。 你同样还可以使用2.9小节的 unicodedata.normalize() 函数将unicode文本标准化。 然后,有时候你可能还想在清理操作上更进一步。比如,你可能想消除整个区间上的字符或者去除变音符。 为了这样做,你可以使用经常会被忽视的 str.translate() 方法。 为了演示,假设你现在有下面这个凌乱的字符串: s = 'pýtĥöñ\fis\tawesome\r\n' s 'pýtĥöñ\x0cis\tawesome\r\n' 第一步是清理空白字符。为了这样做,先创建一个小的转换表格然后使用 translate() 方法: remap = { ... ord('\t') : ' ', ... ord('\f') : ' ', ... ord('\r') : None # Deleted ... } a = s.translate(remap) a 'pýtĥöñ is awesome\n' 正如你看的那样,空白字符 \t 和 \f 已经被重新映射到一个空格。回车字符r直接被删除。 你可以以这个表格为基础进一步构建更大的表格。比如,让我们删除所有的和音符: import unicodedata import sys cmb_chrs = dict.fromkeys(c for c in range(sys.maxunicode) ... if unicodedata.combining(chr(c))) ... b = unicodedata.normalize('NFD', a) b 'pýtĥöñ is awesome\n' b.translate(cmb_chrs) 'python is awesome\n' 上面例子中,通过使用 dict.fromkeys() 方法构造一个字典,每个Unicode和音符作为键,对应的值全部为 None 。 然后使用 unicodedata.normalize() 将原始输入标准化为分解形式字符。 然后再调用 translate 函数删除所有重音符。 同样的技术也可以被用来删除其他类型的字符(比如控制字符等)。 作为另一个例子,这里构造一个将所有Unicode数字字符映射到对应的ASCII字符上的表格: digitmap = { c: ord('0') + unicodedata.digit(chr(c)) ... for c in range(sys.maxunicode) ... if unicodedata.category(chr(c)) == 'Nd' } ... len(digitmap) 460 Arabic digits x = '\u0661\u0662\u0663' x.translate(digitmap) '123' 另一种清理文本的技术涉及到I/O解码与编码函数。这里的思路是先对文本做一些初步的清理, 然后再结合 encode() 或者 decode() 操作来清除或修改它。比如: a 'pýtĥöñ is awesome\n' b = unicodedata.normalize('NFD', a) b.encode('ascii', 'ignore').decode('ascii') 'python is awesome\n' 这里的标准化操作将原来的文本分解为单独的和音符。接下来的ASCII编码/解码只是简单的一下子丢弃掉那些字符。 当然,这种方法仅仅只在最后的目标就是获取到文本对应ACSII表示的时候生效。

哦哦喔 2020-04-16 19:20:07 0 浏览量 回答数 0

回答

每当Python解释器读取源文件时,它就会做两件事: 它设置了一些特殊变量,例如__name__,然后 它执行文件中找到的所有代码。 让我们看看它是如何工作的,以及它与您有关__name__我们在Python脚本中经常看到的检查问题的关系。 代码样例 让我们使用稍微不同的代码示例来探索导入和脚本的工作方式。假设以下文件位于foo.py。 # Suppose this is foo.py. print("before import") import math print("before functionA") def functionA(): print("Function A") print("before functionB") def functionB(): print("Function B {}".format(math.sqrt(100))) print("before __name__ guard") if __name__ == '__main__': functionA() functionB() print("after __name__ guard") 特殊变量 当Python交互程序读取源文件时,它首先定义了一些特殊变量。在这种情况下,我们关心__name__变量。 当您的模块是主程序时 如果您将模块(源文件)作为主程序运行,例如 python foo.py 解释器将硬编码字符串赋值"main"给__name__变量,即 # It's as if the interpreter inserts this at the top # of your module when run as the main program. __name__ = "__main__" 当您的模块由另一个导入时 另一方面,假设其他模块是主程序,并且它将导入您的模块。这意味着在主程序中或主程序导入的某些其他模块中有这样的语句: # Suppose this is in some other main program. import foo 解释器将搜索您的foo.py文件(以及搜索其他一些变体),并在执行该模块之前,它将"foo"导入语句中的名称分配给__name__变量,即 # It's as if the interpreter inserts this at the top # of your module when it's imported from another module. __name__ = "foo" 执行模块的代码 设置特殊变量后,解释器一次执行一个语句,执行模块中的所有代码。您可能想要在代码示例侧面打开另一个窗口,以便您可以按照以下说明进行操作。 总是 它打印字符串"before import"(不带引号)。 它将加载math模块并将其分配给名为的变量math。这等效于替换import math为以下内容(请注意,这__import__是Python中的低级函数,它接受字符串并触发实际的导入): # Find and load a module given its string name, "math", # then assign it to a local variable called math. math = __import__("math") 它输出字符串"before functionA"。 它执行该def块,创建一个功能对象,然后将该功能对象分配给名为的变量functionA。 它输出字符串"before functionB"。 它执行第二个def块,创建另一个功能对象,然后将其分配给名为的变量functionB。 它输出字符串"before name guard"。 仅当您的模块是主程序时 如果您的模块是主程序,那么它将看到__name__确实已将其设置为,"main"并且它将调用两个函数,分别输出字符串"Function A"和"Function B 10.0"。 仅当您的模块由另一个导入时 (相反)如果您的模块不是主程序,而是由另一个程序导入的,__name__则将是"foo",而不是"main",它将跳过if语句的主体。 总是 "after name guard"在两种情况下都将打印字符串。 摘要 总而言之,这是两种情况下的打印内容: # What gets printed if foo is the main program before import before functionA before functionB before __name__ guard Function A Function B 10.0 after __name__ guard # What gets printed if foo is imported as a regular module before import before functionA before functionB before __name__ guard after __name__ guard 为什么这样工作? 您自然会想知道为什么有人会想要这个。好吧,有时您想编写一个.py文件,该文件既可以被其他程序和/或模块用作模块,也可以作为主程序本身运行。例子: 您的模块是一个库,但是您希望有一个脚本模式,在其中运行一些单元测试或演示。 您的模块仅用作主程序,但具有一些单元测试,并且测试框架通过导入.py脚本等文件并运行特殊的测试功能来工作。您不希望它只是因为正在导入模块而尝试运行脚本。 您的模块主要用作主程序,但它也为高级用户提供了程序员友好的API。 除了这些示例之外,可以优雅地用Python运行脚本只是设置一些魔术变量并导入脚本。“运行”脚本是导入脚本模块的副作用。 思想的食物 问题:我可以有多个__name__检查块吗?答:这样做很奇怪,但是这种语言不会阻止您。 假设以下内容在中foo2.py。如果python foo2.py在命令行上说会怎样?为什么? # Suppose this is foo2.py. def functionA(): print("a1") from foo2 import functionB print("a2") functionB() print("a3") def functionB(): print("b") print("t1") if __name__ == "__main__": print("m1") functionA() print("m2") print("t2") 现在,弄清楚如果删除__name__签入会发生什么foo3.py: # Suppose this is foo3.py. def functionA(): print("a1") from foo3 import functionB print("a2") functionB() print("a3") def functionB(): print("b") print("t1") print("m1") functionA() print("m2") print("t2") 当用作脚本时,它将做什么?当作为模块导入时? # Suppose this is in foo4.py __name__ = "__main__" def bar(): print("bar") print("before __name__ guard") if __name__ == "__main__": bar() print("after __name__ guard")

祖安文状元 2020-02-21 14:07:51 0 浏览量 回答数 0

问题

V3版API 签名机制是怎样的?

轩墨 2019-12-01 20:57:42 1487 浏览量 回答数 0

回答

OpenSearch服务会对每个访问的请求进行身份验证,通过使用Access Key ID和Access Key Secret进行对称加密的方法来验证请求的发送者身份。 Access Key ID和Access Key Secret由阿里云官方颁发给访问者(可以通过阿里云官方网站申请和管理),其中Access Key ID用于标识访问者的身份。 Access Key Secret是用于加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密,只有阿里云和用户知道。 支持应用类型 高级版 标准版 通信协议 只支持 HTTP 协议 请求对应方式 搜索数据必须使用:GET 推送数据必须使用:POST Authorization 字段计算方法 需在 HTTP 请求 Header 头信息中,添加 Authorization(授权)来包含签名(Signature)信息,表明该请求已被授权。请求Header 中也需要包含文档下面“签名示例”部分中“请求Header”中提到的这些相关的请求Header。 请求 Header 中包含的参数都必须要参与签名(例如 Content-Md5,Content-Type,Date,Http专有 Header 等等)。 "Authorization: OPENSEARCH " + AccessKeyId + ":" + Signature Signature = base64(hmac-sha1(AccessKeySecret, VERB + "\n" + Content-Md5 + "\n" + Content-Type + "\n" + Date + "\n" + CanonicalizedOpenSearchHeaders + CanonicalizedResource)) 按照RFC2104的定义,使用上面的用于签名的字符串计算签名HMAC值 签名的方法用 RFC 2104 中定义的 HMAC-SHA1 方法 签名的字符串必须为UTF-8格式 含有中文字符的签名字符串必须先进行UTF-8编码,再与AccessKeySecret计算最终签名 签名参数先后顺序,必须和上面保持一致 参数 描述 AccessKeyId 不能为空,请求Header 中的 Authorization 需要用到该 AccessKeyId 值,表示访问指定应用的用户 AccessKeySecret 不能为空,签名所需的秘钥 VERB 不能为空,表示请求操作方法。HTTP 请求 Method,主要有 PUT、GET、POST、HEAD、DELETE 等,不同接口Method也不同 \n 换行符 Content-MD5 请求body有内容时,不能为空。该参数值为,请求body 的MD5值。该请求头用于消息合法性的检查(消息内容是否与发送时一致),例如 4991ef0788236a8f280fed0db928e74e ,对于不发送 body 的请求,例如查询请求,此值请留空。详情参看 RFC2616 Content-MD5 Content-Type application/json Date 不能为空,表示此次操作时间,且必须为 秒级 的 ISO 格式,如2019-02-25T10:09:57Z,时间为UTC时间;如果此时间和 OpenSearch 服务器的时间差正负 15 分钟以上,服务器将拒绝该服务,并返回 HTTP 403 错误 CanonicalizedOpenSearchHeaders 不能为空,用于区分每次请求,以 X-Opensearch- 为前缀的Http专有 Header组合,例如 X-Opensearch-Nonce,在签名过程中这些Http专有 Header名必须全部小写,例如 x-opensearch-nonce,若这些Http专有Header是作为请求Header参数,则需按照原格式名显示 若请求 Header 中不包含这些Http专有 Header,该参数不参与签名计算,在签名方法中直接去掉该参数。 CanonicalizedResource 不能为空,表示用户此次请求路径,例如 /v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 查询请求 请求签名参数 必须 请求 Header 参数 必须 AccessKeySecret 是 Date 是 VERB 是 X-Opensearch-Nonce 是 Date 是 Authorization 是 x-opensearch-nonce 是 canonicalized_resource 是 Header 中的参数值必须要与对应签名方法中的参数值一致 建议将 Content-Md5,Content-Type,Date,CanonicalizedOpenSearchHeaders,Authorization 这些参数都添加到请求Header中,只包含必须参数可能会出现报错,需避免 请求 Header 中包含的参数都必须要参与签名 推送请求 请求签名参数 必须 请求 Header 参数 必须 AccessKeySecret 是 Content-MD5 是 VERB 是 Date 是 Content-MD5 是 Authorization 是 Date 是 canonicalized_resource 是 Header 中的参数值必须要与对应签名方法中的参数值一致 理论上 Content-Md5,Content-Type,Date,CanonicalizedOpenSearchHeaders,Authorization 这些参数都需要添加到请求Header中,只包含必须参数可能会出现报错,需避免 请求 Header 中包含的参数都必须要参与签名 构建CanonicalizedOpenSearchHeaders的方法 所有以 X-Opensearch- 为前缀的 Http专有 Header 被称为 CanonicalizedOpenSearchHeaders,其他非 Http专有 Header 将不被纳入验证 将所有以 X-Opensearch- 为前缀的Http专有 Header 对应的内容补齐,例如X-Opensearch-Nonce : 1551089397451704(该Nonce参数值,可由10位时间戳+6位随机值(100000~999999)组合而成,例如1551089397451704),再去除所有值为空的Http专有 Header 将这些有对应内容值的Http专有 Header 按照名称的字典序进行升序排序 再将这些排序后的专有 Header名,全部转换成小写字母,例如将 X-Opensearch-Nonce : 1551089397451704 转换成 x-opensearch-nonce : 1551089397451704 删除请求头和内容之间分隔符两端出现的任何空格。例如该 x-opensearch-nonce : 1551089397451704 参数,删除两端空格后为:x-opensearch-nonce:1551089397451704 最后将每个请求头及对应内容作为一个单位项,再将每一项之间用 \n 连接拼成最后的 CanonicalizedOpenSearchHeaders,注意最后一个也要有 \n 注意: 若查询请求Header中不包含此处Http专有 Header,即该参数中一个Http专有 Header都没有,则无需 \n,只需在签名方法中去掉该CanonicalizedOpenSearchHeaders签名参数即可,该参数不参与签名计算。 将Http专有 Header添加到Header中时,不能是转换后的小写形式,需按原格式显示 构建CanonicalizedResource的方法 签名的字符串必须为UTF-8格式,且含有中文字符的签名字符串必须先进行 UTF-8 编码,再与 AccessKeySecret 计算最终签名 查询 CanonicalizedResource = path + ? + query 推送 CanonicalizedResource = path 构建 path 部分 对 path 进行urlencode后,再替换 %2F 为 /,下面的app_schema_demo需替换为自己应用名,常见 path 如下所示 search查询path /v3/openapi/apps/app_schema_demo/search suggest查询path /v3/openapi/apps/app_schema_demo/suggest/suggest/search 最终查询指定应用信息查询请求串,下面“appid”需替换为待查询的应用ID,需包含Authorization授权签名参数(无需指定查询参数) /v3/openapi/apps/appid 推送数据path(tab 是要推送到应用中的某个具体表名,也需替换为自己应用表名) /v3/openapi/apps/app_schema_demo/tab/actions/bulk 构建 query 部分 query 部分由查询参数构成,参数为键值对形式 为需要指定的查询参数设置对应的参数值,并去掉value为空的参数(value为空的参数不计算签名) 再对每一个参数按照先比较参数名后比较参数值的顺序,按照字典升序 再对每一部分的参数名和参数值进行 urlencode,再将参数名和对应参数值之间通过=拼接 再将各个查询参数之间用 & 分割拼接并存储到 query 字符串中 再将该 query 字符串中的+字符,替换成%20。注意如果有&&字符,也需替换成&&。 最后按照 path + ? + query 方式拼接至CanonicalizedResource字符串中,即完成查询操作CanonicalizedResource参数构建,示例如下: /v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 以上请求串中主要包含的参数及参数值描述如下: fetch_fields=name 以上请求串中主要包含的query参数中各子句及参数值描述如下(第一个是query参数,第二个是query子句及其它相关子句): query=query=name:'文档'&&sort=id&&config=format:fulljson 注意: query参数中各个查询子句之间必须要用 && 进行拼接 若为推送操作,则只需将 path 部分拼接至 CanonicalizedResource 字符串中即可 构建 Authorization 字段 构建方法参考开头部分描述,需添加到请求 Header 中。假如AccessKeyId 为LTAItQcybixtR9A0,Signature为 1P7tfEh+CU5kFYRXzZ14kkJUAMc=,则python3示例代码大致如下: headers['Authorization'] = 'OPENSEARCH ' + 'LTAItQcybixtR9A0' + ':' + '1P7tfEh+CU5kFYRXzZ14kkJUAMc=' 签名示例 假如参数值如下 Authorization值为OPENSEARCH LTAItQcybixtR9A0:1P7tfEh+CU5kFYRXzZ14kkJUAMc= AccessKeySecret值为 R0OGKsMj0etgyA9nZM5ykhMqHXBfKG 请求方式为GET Content-MD5值为空,此处作为查询请求 Content-Type值为application/json Date值为2019-02-25T10:09:57Z CanonicalizedOpenSearchHeaders值为x-opensearch-nonce:1551089397451704 CanonicalizedResource值为/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 请求Header 签名字符串计算公式 签名字符串 ‘Content-MD5’: ‘’, ‘Content-Type’: ‘application/json’, ‘Authorization’: ‘OPENSEARCH LTAItQcybixtR9A0:1P7tfEh+CU5kFYRXzZ14kkJUAMc=’, ‘X-Opensearch-Nonce’: ‘1551089397451704’, ‘Date’: ‘2019-02-25T10:09:57Z’ Signature = base64(hmac-sha1( AccessKeySecret, VERB + “\n” + Content-Md5 + “\n” + Content-Type + “\n” + Date + “\n” + CanonicalizedOpenSearchHeaders + CanonicalizedResource)) 1P7tfEh+CU5kFYRXzZ14kkJUAMc= , GET\n \n application/json\n 2019-02-25T10:09:57Z\n x-opensearch-nonce:1551089397451704\n /v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 注意: 请求Header中参数值需与签名方法中对应参数值保持一致 可用以下方法计算签名(Signature) 以 hash_hmac 和 base64_encode 编码生成加密值作为Signature python3 示例代码: import hmac import base64 signature_string = '\n'.join(['GET', '', 'application/json', '2019-02-25T10:09:57Z', 'x-opensearch-nonce:1551089397451704', '/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson']) signature_hmac = hmac.new('R0OGKsMj0etgyA9nZM5ykhMqHXBfKG'.encode('utf-8'), signature_string.encode('utf-8'), 'sha1') signature = base64.b64encode(signature_hmac.digest()) 假如AccessKeySecret值为 R0OGKsMj0etgyA9nZM5ykhMqHXBfKG 那么通过上面的签名方法构造出来的签名值为 1P7tfEh+CU5kFYRXzZ14kkJUAMc= 构建请求串 请求串 = host + CanonicalizedResource 需在 HTTP 请求 Header 头信息中增加 Authorization(授权)来包含签名(Signature)信息,表明该请求已被授权,同时Header中也需要包含上面提到的这些相关的请求Header。(host为应用访问API地址) 最终search查询请求串 http://host/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 最终suggest查询请求串 http://host/v3/openapi/apps/app_schema_demo/suggest/suggest/search?query=%E6%A0%87%E9%A2%98&hits=10 最终查询指定应用信息查询请求串,下面最后一部分为“appid”值,假设为120001234该值,替换后如下所示,该查询请求也需包含Authorization授权签名参数(无需指定查询参数) http://host/v3/openapi/apps/120001234 最终推送数据请求串,此处推送数据需放在body体中 http://host/v3/openapi/apps/app_schema_demo/tab/actions/bulk v3API签名Demo 目前已对外公开 v3版官方Java SDK 和 PHP SDK,且PHP SDK已包含v3API签名过程实现源码,直接调用这些方法即可使用,也可参考 PHP SDK 签名实现过程源码,或参考本文档v3API签名过程来实现其它语言SDK。 因目前OpenSearch官方,没有正式对外提供官方的 python版 和 C#版 SDK,为方便用户参考实现该文档描述签名过程,此处提供 python及C#版查询和推送签名Demo,以供用户参考实现本文档签名操作。 注意: 此处提供 Demo 仅供参考,后续用户参考此 Demo 实现的 SDK 由用户自己维护。

保持可爱mmm 2020-03-26 21:56:23 0 浏览量 回答数 0

回答

OpenSearch服务会对每个访问的请求进行身份验证,通过使用Access Key ID和Access Key Secret进行对称加密的方法来验证请求的发送者身份。 Access Key ID和Access Key Secret由阿里云官方颁发给访问者(可以通过阿里云官方网站申请和管理),其中Access Key ID用于标识访问者的身份。 Access Key Secret是用于加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密,只有阿里云和用户知道。 支持应用类型 高级版 标准版 通信协议 只支持 HTTP 协议 请求对应方式 搜索数据必须使用:GET 推送数据必须使用:POST Authorization 字段计算方法 需在 HTTP 请求 Header 头信息中,添加 Authorization(授权)来包含签名(Signature)信息,表明该请求已被授权。请求Header 中也需要包含文档下面“签名示例”部分中“请求Header”中提到的这些相关的请求Header。 请求 Header 中包含的参数都必须要参与签名(例如 Content-Md5,Content-Type,Date,Http专有 Header 等等)。 "Authorization: OPENSEARCH " + AccessKeyId + ":" + Signature Signature = base64(hmac-sha1(AccessKeySecret, VERB + "\n" + Content-Md5 + "\n" + Content-Type + "\n" + Date + "\n" + CanonicalizedOpenSearchHeaders + CanonicalizedResource)) 按照RFC2104的定义,使用上面的用于签名的字符串计算签名HMAC值 签名的方法用 RFC 2104 中定义的 HMAC-SHA1 方法 签名的字符串必须为UTF-8格式 含有中文字符的签名字符串必须先进行UTF-8编码,再与AccessKeySecret计算最终签名 签名参数先后顺序,必须和上面保持一致 参数 描述 AccessKeyId 不能为空,请求Header 中的 Authorization 需要用到该 AccessKeyId 值,表示访问指定应用的用户 AccessKeySecret 不能为空,签名所需的秘钥 VERB 不能为空,表示请求操作方法。HTTP 请求 Method,主要有 PUT、GET、POST、HEAD、DELETE 等,不同接口Method也不同 \n 换行符 Content-MD5 请求body有内容时,不能为空。该参数值为,请求body 的MD5值。该请求头用于消息合法性的检查(消息内容是否与发送时一致),例如 4991ef0788236a8f280fed0db928e74e ,对于不发送 body 的请求,例如查询请求,此值请留空。详情参看 RFC2616 Content-MD5 Content-Type application/json Date 不能为空,表示此次操作时间,且必须为 秒级 的 ISO 格式,如2019-02-25T10:09:57Z,时间为UTC时间;如果此时间和 OpenSearch 服务器的时间差正负 15 分钟以上,服务器将拒绝该服务,并返回 HTTP 403 错误 CanonicalizedOpenSearchHeaders 不能为空,用于区分每次请求,以 X-Opensearch- 为前缀的Http专有 Header组合,例如 X-Opensearch-Nonce,在签名过程中这些Http专有 Header名必须全部小写,例如 x-opensearch-nonce,若这些Http专有Header是作为请求Header参数,则需按照原格式名显示 若请求 Header 中不包含这些Http专有 Header,该参数不参与签名计算,在签名方法中直接去掉该参数。 CanonicalizedResource 不能为空,表示用户此次请求路径,例如 /v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 查询请求 请求签名参数 必须 请求 Header 参数 必须 AccessKeySecret 是 Date 是 VERB 是 X-Opensearch-Nonce 是 Date 是 Authorization 是 x-opensearch-nonce 是 canonicalized_resource 是 Header 中的参数值必须要与对应签名方法中的参数值一致 建议将 Content-Md5,Content-Type,Date,CanonicalizedOpenSearchHeaders,Authorization 这些参数都添加到请求Header中,只包含必须参数可能会出现报错,需避免 请求 Header 中包含的参数都必须要参与签名 推送请求 请求签名参数 必须 请求 Header 参数 必须 AccessKeySecret 是 Content-MD5 是 VERB 是 Date 是 Content-MD5 是 Authorization 是 Date 是 canonicalized_resource 是 Header 中的参数值必须要与对应签名方法中的参数值一致 理论上 Content-Md5,Content-Type,Date,CanonicalizedOpenSearchHeaders,Authorization 这些参数都需要添加到请求Header中,只包含必须参数可能会出现报错,需避免 请求 Header 中包含的参数都必须要参与签名 构建CanonicalizedOpenSearchHeaders的方法 所有以 X-Opensearch- 为前缀的 Http专有 Header 被称为 CanonicalizedOpenSearchHeaders,其他非 Http专有 Header 将不被纳入验证 将所有以 X-Opensearch- 为前缀的Http专有 Header 对应的内容补齐,例如X-Opensearch-Nonce : 1551089397451704(该Nonce参数值,可由10位时间戳+6位随机值(100000~999999)组合而成,例如1551089397451704),再去除所有值为空的Http专有 Header 将这些有对应内容值的Http专有 Header 按照名称的字典序进行升序排序 再将这些排序后的专有 Header名,全部转换成小写字母,例如将 X-Opensearch-Nonce : 1551089397451704 转换成 x-opensearch-nonce : 1551089397451704 删除请求头和内容之间分隔符两端出现的任何空格。例如该 x-opensearch-nonce : 1551089397451704 参数,删除两端空格后为:x-opensearch-nonce:1551089397451704 最后将每个请求头及对应内容作为一个单位项,再将每一项之间用 \n 连接拼成最后的 CanonicalizedOpenSearchHeaders,注意最后一个也要有 \n 注意: 若查询请求Header中不包含此处Http专有 Header,即该参数中一个Http专有 Header都没有,则无需 \n,只需在签名方法中去掉该CanonicalizedOpenSearchHeaders签名参数即可,该参数不参与签名计算。 将Http专有 Header添加到Header中时,不能是转换后的小写形式,需按原格式显示 构建CanonicalizedResource的方法 签名的字符串必须为UTF-8格式,且含有中文字符的签名字符串必须先进行 UTF-8 编码,再与 AccessKeySecret 计算最终签名 查询 CanonicalizedResource = path + ? + query 推送 CanonicalizedResource = path 构建 path 部分 对 path 进行urlencode后,再替换 %2F 为 /,下面的app_schema_demo需替换为自己应用名,常见 path 如下所示 search查询path /v3/openapi/apps/app_schema_demo/search suggest查询path /v3/openapi/apps/app_schema_demo/suggest/suggest/search 最终查询指定应用信息查询请求串,下面“appid”需替换为待查询的应用ID,需包含Authorization授权签名参数(无需指定查询参数) /v3/openapi/apps/appid 推送数据path(tab 是要推送到应用中的某个具体表名,也需替换为自己应用表名) /v3/openapi/apps/app_schema_demo/tab/actions/bulk 构建 query 部分 query 部分由查询参数构成,参数为键值对形式 为需要指定的查询参数设置对应的参数值,并去掉value为空的参数(value为空的参数不计算签名) 再对每一个参数按照先比较参数名后比较参数值的顺序,按照字典升序 再对每一部分的参数名和参数值进行 urlencode,再将参数名和对应参数值之间通过=拼接 再将各个查询参数之间用 & 分割拼接并存储到 query 字符串中 再将该 query 字符串中的+字符,替换成%20。注意如果有&&字符,也需替换成&&。 最后按照 path + ? + query 方式拼接至CanonicalizedResource字符串中,即完成查询操作CanonicalizedResource参数构建,示例如下: /v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 以上请求串中主要包含的参数及参数值描述如下: fetch_fields=name 以上请求串中主要包含的query参数中各子句及参数值描述如下(第一个是query参数,第二个是query子句及其它相关子句): query=query=name:'文档'&&sort=id&&config=format:fulljson 注意: query参数中各个查询子句之间必须要用 && 进行拼接 若为推送操作,则只需将 path 部分拼接至 CanonicalizedResource 字符串中即可 构建 Authorization 字段 构建方法参考开头部分描述,需添加到请求 Header 中。假如AccessKeyId 为LTAItQcybixtR9A0,Signature为 1P7tfEh+CU5kFYRXzZ14kkJUAMc=,则python3示例代码大致如下: headers['Authorization'] = 'OPENSEARCH ' + 'LTAItQcybixtR9A0' + ':' + '1P7tfEh+CU5kFYRXzZ14kkJUAMc=' 签名示例 假如参数值如下 Authorization值为OPENSEARCH LTAItQcybixtR9A0:1P7tfEh+CU5kFYRXzZ14kkJUAMc= AccessKeySecret值为 R0OGKsMj0etgyA9nZM5ykhMqHXBfKG 请求方式为GET Content-MD5值为空,此处作为查询请求 Content-Type值为application/json Date值为2019-02-25T10:09:57Z CanonicalizedOpenSearchHeaders值为x-opensearch-nonce:1551089397451704 CanonicalizedResource值为/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 请求Header 签名字符串计算公式 签名字符串 ‘Content-MD5’: ‘’, ‘Content-Type’: ‘application/json’, ‘Authorization’: ‘OPENSEARCH LTAItQcybixtR9A0:1P7tfEh+CU5kFYRXzZ14kkJUAMc=’, ‘X-Opensearch-Nonce’: ‘1551089397451704’, ‘Date’: ‘2019-02-25T10:09:57Z’ Signature = base64(hmac-sha1( AccessKeySecret, VERB + “\n” + Content-Md5 + “\n” + Content-Type + “\n” + Date + “\n” + CanonicalizedOpenSearchHeaders + CanonicalizedResource)) 1P7tfEh+CU5kFYRXzZ14kkJUAMc= , GET\n \n application/json\n 2019-02-25T10:09:57Z\n x-opensearch-nonce:1551089397451704\n /v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 注意: 请求Header中参数值需与签名方法中对应参数值保持一致 可用以下方法计算签名(Signature) 以 hash_hmac 和 base64_encode 编码生成加密值作为Signature python3 示例代码: import hmac import base64 signature_string = '\n'.join(['GET', '', 'application/json', '2019-02-25T10:09:57Z', 'x-opensearch-nonce:1551089397451704', '/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson']) signature_hmac = hmac.new('R0OGKsMj0etgyA9nZM5ykhMqHXBfKG'.encode('utf-8'), signature_string.encode('utf-8'), 'sha1') signature = base64.b64encode(signature_hmac.digest()) 假如AccessKeySecret值为 R0OGKsMj0etgyA9nZM5ykhMqHXBfKG 那么通过上面的签名方法构造出来的签名值为 1P7tfEh+CU5kFYRXzZ14kkJUAMc= 构建请求串 请求串 = host + CanonicalizedResource 需在 HTTP 请求 Header 头信息中增加 Authorization(授权)来包含签名(Signature)信息,表明该请求已被授权,同时Header中也需要包含上面提到的这些相关的请求Header。(host为应用访问API地址) 最终search查询请求串 http://host/v3/openapi/apps/app_schema_demo/search?fetch_fields=name&query=query%3Dname%3A%27%E6%96%87%E6%A1%A3%27&&sort%3Did&&config%3Dformat%3Afulljson 最终suggest查询请求串 http://host/v3/openapi/apps/app_schema_demo/suggest/suggest/search?query=%E6%A0%87%E9%A2%98&hits=10 最终查询指定应用信息查询请求串,下面最后一部分为“appid”值,假设为120001234该值,替换后如下所示,该查询请求也需包含Authorization授权签名参数(无需指定查询参数) http://host/v3/openapi/apps/120001234 最终推送数据请求串,此处推送数据需放在body体中 http://host/v3/openapi/apps/app_schema_demo/tab/actions/bulk v3API签名Demo 目前已对外公开 v3版官方Java SDK 和 PHP SDK,且PHP SDK已包含v3API签名过程实现源码,直接调用这些方法即可使用,也可参考 PHP SDK 签名实现过程源码,或参考本文档v3API签名过程来实现其它语言SDK。 因目前OpenSearch官方,没有正式对外提供官方的 python版 和 C#版 SDK,为方便用户参考实现该文档描述签名过程,此处提供 python及C#版查询和推送签名Demo,以供用户参考实现本文档签名操作。 注意: 此处提供 Demo 仅供参考,后续用户参考此 Demo 实现的 SDK 由用户自己维护。

保持可爱mmm 2020-03-26 21:56:35 0 浏览量 回答数 0

回答

请求参数排序 按照参数名称的字典顺序对请求中的参数进行排序。(请求参数有:AccessKeyId、Action、Format、JsonStr、RegionId、SignatureMethod、SignatureNonce、SignatureVersion、Timestamp、Version) 对每个请求参数的名称和值进行编码 名称和值要使用UTF-8字符集进行URL编码(需要编码的字段至少包括:JsonStr、Timestamp),URL编码的编码规则是: 对于字符 A-Z、a-z、0-9以及字符“-”、“_”、“.”、“~”不编码; 对于其他字符编码成“%XY”的格式,其中XY是字符对应ASCII码的16进制表示。比如英文的双引号(”)对应的编码是%22 对于扩展的UTF-8字符,编码成“%XY%ZA…”的格式; 需要说明的是英文空格( )要被编码是%20,而不是加号(+)。 注:一般支持URL编码的库(比如Java中的java.net.URLEncoder)都是按照“application/x-www-form-urlencoded”的MIME类型的规则进行编码的。实现时可以借助该方法,把编码后的字符串中加号(+)替换成%20、星号(*)替换成%2A、%7E替换回波浪号(~),即可得到上述规则描述的编码字符串。 参考代码(java): public String percentEncode(String o) throws UnsupportedEncodingException { return URLEncoder.encode(o, "utf8").replace("+", "%20") .replace("*", "%2A") .replace("%7E", "~"); } 对编码后的参数名称和值使用英文等号(=)进行连接。 将英文等号连接得到的字符串按参数名称的字典顺序依次使用&符号连接,即得到规范化请求字符串。 编码后的字符串( CanonicalizedQueryString )如下: &AccessKeyId=lFKEO3Q10JCIGvNv&Action=GetAudioDataStatus&Format=JSON&JsonStr=%7B%22appKey%22%3A1733149043164104%2C%22taskId%22%3A%22B8578666-7136-49A9-9DA0-3B3732DAFF62%22%7D&RegionId=cn-hangzhou&SignatureMethod=HMAC-SHA1&SignatureNonce=1c550238-8a54-46a0-b8c4-666237b1e399&SignatureVersion=1.0&Timestamp=2018-02-06T08%3A50%3A58Z&Version=2016-08-01 计算签名签名 ( StringToSign )的构造规则为: HTTPMethod + “&” + percentEncode(“/”) + ”&” + percentEncode(CanonicalizedQueryString) 其中, HTTPMethod是提交请求用的HTTP方法,比如:GET、POST。 percentEncode(“/”)是按照URL编码规则对字符“/”进行编码得到的值,即“%2F”。 percentEncode(CanonicalizedQueryString)是对上步中CanonicalizedQueryString的字符串按URL编码规则编码后得到的字符串。 按照RFC2104的定义,使用上面用于签名的字符串计算签名HMAC值。 注意:计算签名时使用的Key就是用户持有的Access Key Secret并加上一个“&”字符(ASCII:38),使用的哈希算法是SHA1。 按照Base64编码规则把上面的HMAC值编码成字符串,即得到签名值(Signature)。 示例代码 Java: public String signString(String StringToSign, String accessSecret) { Mac mac = Mac.getInstance("HmacSHA1"); mac.init(new SecretKeySpec( accessSecret.getBytes("UTF-8"),"HmacSHA1")); byte[] signData = mac.doFinal(StringToSign.getBytes("UTF-8")); return Base64Helper.encode(signData); } python2: import hashlib import string from hashlib import sha1 import base64 import hmac from hmac import new as hmac def hash_hmac(ac_key, text): h = hmac(ac_key, text, sha1) d = h.digest() print 'd: ' + d return str(d.encode('base64')) python3: import base64 import hmac from hashlib import sha1 def hash_hmac(code, key, sha1): hmac_code = hmac.new(key.encode(), code.encode(), sha1).digest() return base64.b64encode(hmac_code).decode() 将得到的签名值作为Signature参数添加到请求参数中,即完成对请求签名的过程。 注意:得到的签名值在作为最后的请求参数值提交时,要和其他参数一样,按照RFC3986的规则进行URL编码)。 示例 签名前的请求URL为: http://qualitycheck.cn-hangzhou.aliyuncs.com/?JsonStr={"appKey":1733149043164104,"taskId":"B8578666-7136-49A9-9DA0-3B3732DAFF62"}&SignatureVersion=1.0 &Action=GetAudioDataStatus&Format=JSON&SignatureNonce=1c550238-8a54-46a0-b8c4-666237b1e399 &Version=2016-08-01&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&RegionId=cn-hangzhou &Timestamp=2018-02-06T08:50:58Z 那么StringToSign为: GET&%2F&AccessKeyId%3Dtestid&Action%3DGetAudioDataStatus&Format%3DJSON&JsonStr%3D%257B%2522appKey%2522%253A%25221733149043164104%2522%252C%2522taskId%2522%253A%2522B8578666-7136-49A9-9DA0-3B3732DAFF62%2522%257D&RegionId%3Dcn-hangzhou&SignatureMethod%3DHMAC-SHA1&SignatureNonce%3D1c550238-8a54-46a0-b8c4-666237b1e399&SignatureVersion%3D1.0&Timestamp%3D2018-02-06T08%253A50%253A58Z&Version%3D2016-08-01 如使用的Access Key Id是“testid”,Access Key Secret是“testsecret”,用于计算HMAC的Key就是“testsecret&”,则计算得到的签名值是: MQIWlE70sNCpDsRRKTpOvdQcME8= 签名后的请求URL为(注意增加了Signature参数): http://qualitycheck.cn-hangzhou.aliyuncs.com/ ?JsonStr={"appKey":1733149043164104,"taskId":"B8578

保持可爱mmm 2020-03-26 22:32:29 0 浏览量 回答数 0

回答

在这个问题中,我们集中讨论根据特殊语法去解析文本的问题。为了这样做,你首先要以BNF或者EBNF形式指定一个标准语法。比如,一个简单数学表达式语法可能像下面这样: expr ::= expr + term | expr - term | term term ::= term * factor | term / factor | factor factor ::= ( expr ) | NUM 或者,以EBNF形式: expr ::= term { (+|-) term }* term ::= factor { (|/) factor } factor ::= ( expr ) | NUM 在EBNF中,被包含在 {...}* 中的规则是可选的。*代表0次或多次重复(跟正则表达式中意义是一样的)。 现在,如果你对BNF的工作机制还不是很明白的话,就把它当做是一组左右符号可相互替换的规则。一般来讲,解析的原理就是你利用BNF完成多个替换和扩展以匹配输入文本和语法规则。为了演示,假设你正在解析形如 3 + 4 * 5 的表达式。这个表达式先要通过使用2.18节中介绍的技术分解为一组令牌流。结果可能是像下列这样的令牌序列: NUM + NUM * NUM 在此基础上, 解析动作会试着去通过替换操作匹配语法到输入令牌: expr expr ::= term { (+|-) term }* expr ::= factor { (|/) factor } { (+|-) term }* expr ::= NUM { (|/) factor } { (+|-) term }* expr ::= NUM { (+|-) term }* expr ::= NUM + term { (+|-) term }* expr ::= NUM + factor { (|/) factor } { (+|-) term }* expr ::= NUM + NUM { (|/) factor} { (+|-) term }* expr ::= NUM + NUM * factor { (|/) factor } { (+|-) term }* expr ::= NUM + NUM * NUM { (|/) factor } { (+|-) term }* expr ::= NUM + NUM * NUM { (+|-) term }* expr ::= NUM + NUM * NUM 下面所有的解析步骤可能需要花点时间弄明白,但是它们原理都是查找输入并试着去匹配语法规则。第一个输入令牌是NUM,因此替换首先会匹配那个部分。一旦匹配成功,就会进入下一个令牌+,以此类推。当已经确定不能匹配下一个令牌的时候,右边的部分(比如 { (/) factor } )就会被清理掉。在一个成功的解析中,整个右边部分会完全展开来匹配输入令牌流。 有了前面的知识背景,下面我们举一个简单示例来展示如何构建一个递归下降表达式求值程序: #!/usr/bin/env python -- encoding: utf-8 -- """ Topic: 下降解析器 Desc : """ import re import collections Token specification NUM = r'(?P \d+)' PLUS = r'(?P +)' MINUS = r'(?P -)' TIMES = r'(?P *)' DIVIDE = r'(?P /)' LPAREN = r'(?P ()' RPAREN = r'(?P ))' WS = r'(?P \s+)' master_pat = re.compile('|'.join([NUM, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN, WS])) Tokenizer Token = collections.namedtuple('Token', ['type', 'value']) def generate_tokens(text): scanner = master_pat.scanner(text) for m in iter(scanner.match, None): tok = Token(m.lastgroup, m.group()) if tok.type != 'WS': yield tok Parser class ExpressionEvaluator: ''' Implementation of a recursive descent parser. Each method implements a single grammar rule. Use the ._accept() method to test and accept the current lookahead token. Use the ._expect() method to exactly match and discard the next token on on the input (or raise a SyntaxError if it doesn't match). ''' def parse(self, text): self.tokens = generate_tokens(text) self.tok = None # Last symbol consumed self.nexttok = None # Next symbol tokenized self._advance() # Load first lookahead token return self.expr() def _advance(self): 'Advance one token ahead' self.tok, self.nexttok = self.nexttok, next(self.tokens, None) def _accept(self, toktype): 'Test and consume the next token if it matches toktype' if self.nexttok and self.nexttok.type == toktype: self._advance() return True else: return False def _expect(self, toktype): 'Consume next token if it matches toktype or raise SyntaxError' if not self._accept(toktype): raise SyntaxError('Expected ' + toktype) # Grammar rules follow def expr(self): "expression ::= term { ('+'|'-') term }*" exprval = self.term() while self._accept('PLUS') or self._accept('MINUS'): op = self.tok.type right = self.term() if op == 'PLUS': exprval += right elif op == 'MINUS': exprval -= right return exprval def term(self): "term ::= factor { ('*'|'/') factor }*" termval = self.factor() while self._accept('TIMES') or self._accept('DIVIDE'): op = self.tok.type right = self.factor() if op == 'TIMES': termval *= right elif op == 'DIVIDE': termval /= right return termval def factor(self): "factor ::= NUM | ( expr )" if self._accept('NUM'): return int(self.tok.value) elif self._accept('LPAREN'): exprval = self.expr() self._expect('RPAREN') return exprval else: raise SyntaxError('Expected NUMBER or LPAREN') def descent_parser(): e = ExpressionEvaluator() print(e.parse('2')) print(e.parse('2 + 3')) print(e.parse('2 + 3 * 4')) print(e.parse('2 + (3 + 4) * 5')) # print(e.parse('2 + (3 + * 4)')) # Traceback (most recent call last): # File " ", line 1, in # File "exprparse.py", line 40, in parse # return self.expr() # File "exprparse.py", line 67, in expr # right = self.term() # File "exprparse.py", line 77, in term # termval = self.factor() # File "exprparse.py", line 93, in factor # exprval = self.expr() # File "exprparse.py", line 67, in expr # right = self.term() # File "exprparse.py", line 77, in term # termval = self.factor() # File "exprparse.py", line 97, in factor # raise SyntaxError("Expected NUMBER or LPAREN") # SyntaxError: Expected NUMBER or LPAREN if name == 'main': descent_parser() 讨论 文本解析是一个很大的主题, 一般会占用学生学习编译课程时刚开始的三周时间。如果你在找寻关于语法,解析算法等相关的背景知识的话,你应该去看一下编译器书籍。很显然,关于这方面的内容太多,不可能在这里全部展开。 尽管如此,编写一个递归下降解析器的整体思路是比较简单的。开始的时候,你先获得所有的语法规则,然后将其转换为一个函数或者方法。因此如果你的语法类似这样: expr ::= term { ('+'|'-') term }* term ::= factor { (''|'/') factor } factor ::= '(' expr ')' | NUM 你应该首先将它们转换成一组像下面这样的方法: class ExpressionEvaluator: ... def expr(self): ... def term(self): ... def factor(self): ... 每个方法要完成的任务很简单 - 它必须从左至右遍历语法规则的每一部分,处理每个令牌。从某种意义上讲,方法的目的就是要么处理完语法规则,要么产生一个语法错误。为了这样做,需采用下面的这些实现方法: 如果规则中的下个符号是另外一个语法规则的名字(比如term或factor),就简单的调用同名的方法即可。这就是该算法中”下降”的由来 - 控制下降到另一个语法规则中去。有时候规则会调用已经执行的方法(比如,在 factor ::= '('expr ')' 中对expr的调用)。这就是算法中”递归”的由来。 如果规则中下一个符号是个特殊符号(比如(),你得查找下一个令牌并确认是一个精确匹配)。如果不匹配,就产生一个语法错误。这一节中的 _expect() 方法就是用来做这一步的。 如果规则中下一个符号为一些可能的选择项(比如 + 或 -),你必须对每一种可能情况检查下一个令牌,只有当它匹配一个的时候才能继续。这也是本节示例中 _accept() 方法的目的。它相当于_expect()方法的弱化版本,因为如果一个匹配找到了它会继续,但是如果没找到,它不会产生错误而是回滚(允许后续的检查继续进行)。 对于有重复部分的规则(比如在规则表达式 ::= term { ('+'|'-') term }* 中),重复动作通过一个while循环来实现。循环主体会收集或处理所有的重复元素直到没有其他元素可以找到。 一旦整个语法规则处理完成,每个方法会返回某种结果给调用者。这就是在解析过程中值是怎样累加的原理。比如,在表达式求值程序中,返回值代表表达式解析后的部分结果。最后所有值会在最顶层的语法规则方法中合并起来。 尽管向你演示的是一个简单的例子,递归下降解析器可以用来实现非常复杂的解析。比如,Python语言本身就是通过一个递归下降解析器去解释的。如果你对此感兴趣,你可以通过查看Python源码文件Grammar/Grammar来研究下底层语法机制。看完你会发现,通过手动方式去实现一个解析器其实会有很多的局限和不足之处。 其中一个局限就是它们不能被用于包含任何左递归的语法规则中。比如,加入你需要翻译下面这样一个规则: items ::= items ',' item | item 为了这样做,你可能会像下面这样使用 items() 方法: def items(self): itemsval = self.items() if itemsval and self._accept(','): itemsval.append(self.item()) else: itemsval = [ self.item() ] 唯一的问题是这个方法根本不能工作,事实上,它会产生一个无限递归错误。 关于语法规则本身你可能也会碰到一些棘手的问题。比如,你可能想知道下面这个简单扼语法是否表述得当: expr ::= factor { ('+'|'-'|''|'/') factor } factor ::= '(' expression ')' | NUM 这个语法看上去没啥问题,但是它却不能察觉到标准四则运算中的运算符优先级。比如,表达式 "3 + 4 * 5" 会得到35而不是期望的23.分开使用”expr”和”term”规则可以让它正确的工作。 对于复杂的语法,你最好是选择某个解析工具比如PyParsing或者是PLY。下面是使用PLY来重写表达式求值程序的代码: from ply.lex import lex from ply.yacc import yacc Token list tokens = [ 'NUM', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'LPAREN', 'RPAREN' ] Ignored characters t_ignore = ' \t\n' Token specifications (as regexs) t_PLUS = r'+' t_MINUS = r'-' t_TIMES = r'*' t_DIVIDE = r'/' t_LPAREN = r'(' t_RPAREN = r')' Token processing functions def t_NUM(t): r'\d+' t.value = int(t.value) return t Error handler def t_error(t): print('Bad character: {!r}'.format(t.value[0])) t.skip(1) Build the lexer lexer = lex() Grammar rules and handler functions def p_expr(p): ''' expr : expr PLUS term | expr MINUS term ''' if p[2] == '+': p[0] = p[1] + p[3] elif p[2] == '-': p[0] = p[1] - p[3] def p_expr_term(p): ''' expr : term ''' p[0] = p[1] def p_term(p): ''' term : term TIMES factor | term DIVIDE factor ''' if p[2] == '*': p[0] = p[1] * p[3] elif p[2] == '/': p[0] = p[1] / p[3] def p_term_factor(p): ''' term : factor ''' p[0] = p[1] def p_factor(p): ''' factor : NUM ''' p[0] = p[1] def p_factor_group(p): ''' factor : LPAREN expr RPAREN ''' p[0] = p[2] def p_error(p): print('Syntax error') parser = yacc() 这个程序中,所有代码都位于一个比较高的层次。你只需要为令牌写正则表达式和规则匹配时的高阶处理函数即可。而实际的运行解析器,接受令牌等等底层动作已经被库函数实现了。 下面是一个怎样使用得到的解析对象的例子: parser.parse('2') 2 parser.parse('2+3') 5 parser.parse('2+(3+4)*5') 37

景凌凯 2020-04-16 19:33:06 0 浏览量 回答数 0

回答

概述 对于每一次 HTTP 或者 HTTPS 协议请求,我们会根据访问中的签名信息验证访问请求者身份。具体由使用 AccessKeyID 和 AccessKeySecret 对称加密验证实现。 AccessKeyID 和 AccessKeySecret 可在控制台 AccessKey 管理 页面获得。其中 AccessKeyID 是访问者身份,AccessKeySecret 是加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密谨防泄露。 说明:我们提供了多种编程语言的 SDK 及第三方 SDK,可以免去您签名的烦恼。更多详情,请下载 SDK。 步骤 1. 构造规范化请求字符串 排序参数。排序规则以首字母顺序排序,排序参数包括 公共请求参数 和接口自定义参数,不包括公共请求参数中的 Signature 参数。 说明:当使用 GET 方法提交请求时,这些参数就是请求 URL 中的参数部分。即 URL 中 ? 之后由 & 连接的部分。 编码参数。使用 UTF-8 字符集按照 RFC3986 规则编码请求参数和参数取值,编码规则如下: 字符 A~Z、a~z、0~9 以及字符 -、_、.、~ 不编码。 其它字符编码成 %XY 的格式,其中 XY 是字符对应 ASCII 码的 16 进制。示例:半角双引号(")对应 %22。 扩展的 UTF-8 字符,编码成 %XY%ZA… 的格式。 空格( )编码成 %20,而不是加号(+)。 该编码方式与 application/x-www-form-urlencoded MIME 格式编码算法相似,但又有所不同。 如果您使用的是 Java 标准库中的 java.net.URLEncoder,可以先用标准库中 percentEncode 编码,随后将编码后的字符中加号(+)替换为 %20、星号(*)替换为 %2A、%7E 替换为波浪号(~),即可得到上述规则描述的编码字符串。 使用等号(=)连接编码后的请求参数和参数取值。 使用与号(&)连接编码后的请求参数,注意参数排序与 步骤 1 一致。 步骤 2. 构造签名字符串 构造待签名字符串 StringToSign。 您可以同样使用 percentEncode 处理上一步构造的规范化请求字符串,规则如下: StringToSign= HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。 percentEncode("/") + "&" + //percentEncode("/"):字符(/)UTF-8 编码得到的值,即 %2F。 percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。 计算 HMAC 值。 按照 RFC2104 的定义,使用上述步骤得到的字符串计算签名 HMAC 值。 注意:计算签名时使用的 Key 就是您持有的 AccessKeySecret 并加上一个 & 字符(ASCII:38),使用的哈希算法是 SHA1。 计算签名值。 按照 Base64 编码规则 把上一步骤中的 HMAC 值编码成字符串,即得到签名值(Signature)。 添加签名。 将得到的签名值作为 Signature 参数添加到请求参数中,即完成请求签名过程。 注意:得到的签名值在作为最后的请求参数值提交给服务器时,也要按照 RFC3986 规则进行 URL 编码。 请求示例 以 GetVideoPlayAuth 为例,假设使用的 AccessKeyId 为 testAccessKeyId, AccessKeySecret 为 testAccessKeySecret。 那么签名前的请求 URL 为: http://vod.cn-shanghai.aliyuncs.com/?TimeStamp=2017-10-10T12:02:54Z&Format=JSON&AccessKeyId=testAccessKeyId&Action=GetVideoPlayAuth&SignatureMethod=HMAC-SHA1&SignatureNonce=8f8a035d-6496-4268-afd4-67c22837e38d&Version=2017-03-21&SignatureVersion=1.0&VideoId=5aed81b74ba84920be578cdfe004af4b 计算得到的待签名字符串 StringToSign 为: GET&%2F&AccessKeyId%3DtestAccessKeyId&Action%3DGetVideoPlayAuth&Format%3DJSON&SignatureMethod%3DHMAC-SHA1&SignatureNonce%3D8f8a035d-6496-4268-afd4-67c22837e38d&SignatureVersion%3D1.0&Timestamp%3D2017-10-10T12%253A02%253A54Z&Version%3D2017-03-21&VideoId%3D5aed81b74ba84920be578cdfe004af4b 因为 AccessKeySecret 为 testAccessKeySecret,所以用于计算 HMAC 的 Key 为 testAccessKeySecret&,计算得到的签名值为: Ibgh7y8Vp47LBuAsf5Xhi1SvDss%3D 将签名作为 Signature 参数加入到 URL 请求中,最后得到的 URL 为: http://vod.cn-shanghai.aliyuncs.com?AccessKeyId=testAccessKeyId&Action=GetVideoPlayAuth&Format=JSON&SignatureMethod=HMAC-SHA1&SignatureNonce=8f8a035d-6496-4268-afd4-67c22837e38d&SignatureVersion=1.0&Timestamp=2017-10-10T12%3A02%3A54Z&Version=2017-03-21&VideoId=5aed81b74ba84920be578cdfe004af4b&Signature=Ibgh7y8Vp47LBuAsf5Xhi1SvDss%3D Java 示例代码 以下将为您介绍OpenAPI公共参数中需要您通过代码生成的参数,其他参数请根据文档填写具体值(本示例不需要依赖第三方的库包,可以直接使用)。 1. 生成签名串Signature 构造签名字符串分为以下几个步骤进行: 1.1. 构造规范化的请求字符串 参数排序及URL编码。其中,参数排序是指将参数名称以字母序升序排列。 /*对所有参数名称和参数值做URL编码*/ public static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) { List<String> encodeParams = new ArrayList<String>(); if (publicParams != null) { for (String key : publicParams.keySet()) { String value = publicParams.get(key); //将参数和值都urlEncode一下。 String encodeKey = percentEncode(key); String encodeVal = percentEncode(value); encodeParams.add(encodeKey + "=" + encodeVal); } } if (privateParams != null) { for (String key : privateParams.keySet()) { String value = privateParams.get(key); //将参数和值都urlEncode一下。 String encodeKey = percentEncode(key); String encodeVal = percentEncode(value); encodeParams.add(encodeKey + "=" + encodeVal); } } return encodeParams; } /*获取 CanonicalizedQueryString*/ public static String getCQS(List<String> allParams) { ParamsComparator paramsComparator = new ParamsComparator(); Collections.sort(allParams, paramsComparator); String cqString = ""; for (int i = 0; i < allParams.size(); i++) { cqString += allParams.get(i); if (i != allParams.size() - 1) { cqString += "&"; } } return cqString; } /*字符串参数比较器,按字母序升序*/ public static class ParamsComparator implements Comparator<String> { @Override public int compare(String lhs, String rhs) { return lhs.compareTo(rhs); } } 1.2. 构造待签名的字符串 /*构造待签名的字符串*/ String StringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString); /*特殊字符替换为转义字符*/ public static String percentEncode(String value) { try { String urlEncodeOrignStr = URLEncoder.encode(value, "UTF-8"); String plusReplaced = urlEncodeOrignStr.replace("+", "%20"); String starReplaced = plusReplaced.replace("*", "%2A"); String waveReplaced = starReplaced.replace("%7E", "~"); return waveReplaced; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return value; } 1.3. 计算待签名字符串的HMAC值 public static byte[] hmacSHA1Signature(String accessKeySecret, String stringToSign) { try { String key = accessKeySecret + "&"; try { SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(signKey); return mac.doFinal(stringToSign.getBytes()); } catch (Exception e) { throw new SignatureException("Failed to generate HMAC : " + e.getMessage()); } } catch (SignatureException e) { e.printStackTrace(); } return null; } 1.4. 编码得到最终签名值 按照 Base64 编码规则将1.3中计算得到的HMAC值编码成字符串,得到最终签名值(Signature)。 public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException { if (bytes == null || bytes.length == 0) { return null; } return new String(new BASE64Encoder().encode(bytes)); } 生成时间戳TimeStamp 生成TimeStamp,为UTC时间戳,如:2017-10-10T12:02:54Z /*生成当前UTC时间戳Time*/ public static String generateTimestamp() { Date date = new Date(System.currentTimeMillis()); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); df.setTimeZone(new SimpleTimeZone(0, "GMT")); return df.format(date); } 生成随机数SignatureNonce public static String generateRandom() { String signatureNonce = UUID.randomUUID().toString(); return signatureNonce; } 至此,已经为您重点介绍了如何生成OpenAPI核心参数的代码示例,为了方便您快速使用签名机制,您可以在 OpenAPI调用示例 下载完整的OpenAPI调用示例Java代码。 更多参考 服务端SDK 可以直接使用服务端接口SDK,来避免自行实现签名机制,SDK支持以下语言版本: Java SDK Python SDK PHP SDK .NET SDK Node.js SDK Go SDK C/C++ SDK 服务端接口SDK封装了 点播服务API 的使用,也提供了各接口的代码示例。 其它签名示例 如确实想自行计算签名,可参考阿里云SDK的签名实现代码: PHP 签名示例 Python签名示例 .NET 签名示例 Go 签名示例 Node.js 签名示例 C/C++ 签名示例

保持可爱mmm 2020-03-30 12:07:33 0 浏览量 回答数 0

回答

本篇主要是介绍如何将渲染软件 Blender 创建成 BatchCompute 的 App,并通过此 App 提交 Blender 渲染作业。 Blender 是目前最流行的一款开源的跨平台全能三维动画制作软件,提供从建模、动画、材质、渲染、到音频处理、视频剪辑等一系列动画短片制作解决方案。 具体介绍可以看这里:https://www.blender.org/features/。 准备工作 (1) 开通服务 开通批量计算服务(BatchCompute): https://help.aliyun.com/document_detail/127644.html 开通对象存储服务(OSS): https://oss.console.aliyun.com 开通MNS服务: https://mns.console.aliyun.com 开通容器服务: https://cr.console.aliyun.com 如果已经开通,请忽略此步骤。 (2) 地域的选择 本篇例子所有阿里云服务都需要使用相同的地域。 本篇例子使用地域: 华南1(深圳) (3) 准备OSS Bucket 请到OSS控制台 创建一个Bucket。 本篇例子假设创建的 bucket 名称为:blender-demo, 地域在华南1(深圳)。 注意: 使用批量计算时,地域需要和 OSS bucket 的地域相同。 注意: 实际操作时,需要将例子中的bucket 名称修改为您自己创建的真实的bucket名称。 制作 Blender Docker 镜像 (1) 创建一个Dockerfile文件 文件名:Dockerfile, 内容如下: FROM ubuntu:latest MAINTAINER your-name 更新源 RUN apt update 清除缓存 RUN apt autoclean 安装 RUN apt install python python-pip curl pulseaudio blender -y 启动时运行这个命令 CMD ["/bin/bash"] (2) build docker build -t ubuntu-blender ./ 等待完成,然后使用下面的命令查看是否有 ubuntu-blender docker images (3) check docker run -t ubuntu-blender blender -v 显示: Blender 2.79 (sub 0) 记住此版本信息,下面要用到。 Docker镜像上传 您需要将 ubuntu-blender 上传到 BatchCompute 支持Registry。 BatchCompute支持2种Registry:阿里云的 CR(Container Registry)和阿里云的OSS。 选择一种即可,推荐第一种: CR。 如何上传,请参考这2篇文档: docker 镜像上传到CR docker 镜像上传到OSS 假设已经上传到CR(地域:华南1-深圳),名称为: registry.cn-shenzhen.aliyuncs.com/batchcompute_test/blender:1.0 创建 App BatchCompute 提交作业,需要配置很多参数。BatchCompute 提供的 App 模板机制,让用户很方便预设参数默认值,提交作业时,只需填写少量参数即可。 下面我们来创建一个 Blender 渲染 App。 (1) 开始创建 App 打开批量计算控制台: https://batchcompute.console.aliyun.com 1 填写基本信息 Docker 镜像名称,填写您已经上传到CR的镜像名称,如: registry.cn-shenzhen.aliyuncs.com/batchcompute_test/blender:1.0 2 运行时参数 只需修改 实例类型为 8核16GB规格,其他的默认即可。 3 (2) 命令行和参数配置 4 命令行填写: python -c "import os;import sys;sys.path.append('/home/scripts/'); from framer import parseFrames; frames=parseFrames('${frames}'); framestr=','.join(map(lambda x:str(x), frames)); s='blender -b /home/input/${scene_file_path} -o /home/output/result/${output_name_format} -F ${format} -f %s' % framestr; print('exec: %s' % s); os.system(s);" ${..} 都是变量,可以作为输入和输出参数。在使用此App提交作业的时候,传入的参数将替换掉这些变量。 参考文档 Blender 2.79 命令行参数 输入参数 注意: 实际操作时,需要将例子中的bucket 名称修改为您自己创建的真实的bucket名称。 名称 默认值 允许覆盖 本地目录绝对路径 备注 scripts_oss_folder oss://blender-demo/scripts/ 否 /home/scripts/ 输入oss目录路径,该路径将挂载到虚拟机的/home/scripts/,应该包含要渲染的 framer.py 文件, 如: oss://bucket/scripts/ input_oss_folder 是 /home/input/ 输入oss目录路径,该路径将挂载到虚拟机的/home/input/,应该包含要渲染的.blend文件, 如: oss://bucket/input/ scene_file_path 是 渲染场景文件的路径,相对于input_oss_folder的目录路径, 如:a.blend 或者 folder_name/a.blend frames 是 支持连续帧:”1-10”, 支持多帧(逗号隔开,无空格):”1,3,5-10” format PNG 是 渲染输出格式,支持: TGA,RAWTGA,JPEG,IRIS,IRIZ,AVIRAW,AVIJPEG,PNG,BMP output_name_format ####.png 是 输出文件名,#会被替代为帧序号,不足位补零。举例: test_###.png 变成 test_001.png,可以在前面加目录名: test/test_###.png 输出参数 名称 默认值 允许覆盖 本地目录绝对路径 备注 output_oss_folder 是 /home/output/ 输出oss目录路径, 如: oss://bucket/output/。 环境变量 环境变量可以不用配置, 直接提交即可。 提交渲染作业 在App列表中可以看到已经创建好的 ubuntu-blender, 点击”提交作业”。 5 (1) 准备工作 在提交作业前,还有一些准备工作。 手动上传分帧器 分帧器python代码(见附录),上传到您的OSS目录下,比如: oss://blender-demo/scripts/framer.py 手动上传blender场景文件 Blender 官网提供了好多 demo 文件: https://www.blender.org/download/demo-files/ 本例子需要下载 2.79 版本(注意:要和镜像中安装的Blender版本相同。不同版本的可能渲染不出来) 6 素材下载后,解压得到目录: splash279/ 将整个目录上传到 oss://blender-demo/input/ 下面,即:oss://blender-demo/input/splash279/。 (2) 开始提交作业 7 实例类型要选大一点的,比如: 8核16GB。 实例数量本例子填 2 个。 (3) 参数配置 注意: 实际操作时,需要将例子中的 bucket 名称修改为您自己创建的真实的bucket名称。 8 输入: 参数 值 说明 input_oss_folder oss://blender-demo/input/ 场景文件所在OSS目录 scene_file_path splash279/splash279.blend 场景文件名 frames 1-4 渲染1到4帧 format PNG 渲染输出格式,默认即可 output_name_format ####.png 渲染输出文件名,默认即可 scripts_oss_folder 设置了默认值,且不允许覆盖,可以不用填。 输出: 参数 值 说明 input_oss_folder oss://blender-demo/output/ 输出OSS目录, 渲染结果图片将保存到此目录的 result/ 子目录下 Loggin(日志目录配置): 参数 值 说明 StdoutPath oss://blender-demo/log/ stdout日志输出到此 StderrPath oss://blender-demo/log/ stderr日志输出到此 填好后点击提交即可。 查看作业状态和结果 (1) 查看作业状态 9 (2) 查看结果 oss://blender-demo/output/result/ 10 (3) 渲染时长和实例规格参考 实例规格 节点数 渲染帧数 时长 ecs.sn1ne.2xlarge(8核16GB) 2 1-4 9-12分钟 ecs.sn1ne.4xlarge (16核/32GB) 2 1-4 4-6分钟 6. 附录 分帧器代码(python): framer.py: #!/usr/bin/python -- coding: UTF-8 -- import os import math import sys import re NOTHING_TO_DO = 'Nothing to do, exit' def _calcRange(a,b, id, step): start = min(id * step + a, b) end = min((id+1) * step + a-1, b) return (start, end) def _parseContinuedFrames(render_frames, total_nodes, id=None, return_type='list'): ''' 解析连续帧, 如: 1-10 ''' [a,b]=render_frames.split('-') a=int(a) b=int(b) #print(a,b) step = int(math.ceil((b-a+1)*1.0/total_nodes)) #print('step:', step) mod = (b-a+1) % total_nodes #print('mod:', mod) if mod==0 or id < mod: (start, end) = _calcRange(a,b, id, step) #print('--->',start, end) return (start, end) if return_type!='list' else range(start, end+1) else: a1 = step * mod + a #print('less', a1, b, id) (start, end) = _calcRange(a1 ,b, id-mod, step-1) #print('--->',start, end) return (start, end) if return_type!='list' else range(start, end+1) def _parseIntermittentFrames(render_frames, total_nodes, id=None): ''' 解析不连续帧, 如: 1,3,8-10,21 ''' a1=render_frames.split(',') a2=[] for n in a1: a=n.split('-') a2.append(range(int(a[0]),int(a[1])+1) if len(a)==2 else [int(a[0])]) a3=[] for n in a2: a3=a3+n #print('a3',a3) step = int(math.ceil(len(a3)*1.0/total_nodes)) #print('step',step) mod = len(a3) % total_nodes #print('mod:', mod) if mod==0 or id < mod: (start, end) = _calcRange(0, len(a3)-1, id, step) #print(start, end) a4= a3[start: end+1] #print('--->', a4) return a4 else: #print('less', step * mod , len(a3)-1, id) (start, end) = _calcRange( step * mod ,len(a3)-1, id-mod, step-1) if start > len(a3)-1: print(NOTHING_TO_DO) sys.exit(0) #print(start, end) a4= a3[start: end+1] #print('--->', a4) return a4 def parseFrames(render_frames, return_type='list', id=None, total_nodes=None): ''' @param render_frames {string}: 需要渲染的总帧数列表范围,可以用"-"表示范围,不连续的帧可以使用","隔开, 如: 1,3,5-10 @param return_type {string}: 取值范围[list,range]。 list样例: [1,2,3], range样例: (1,3)。 注意: render_frames包含","时有效,强制为list。 @param id, 节点ID,从0开始。 正式环境不要填写,将从环境变量 BATCH_COMPUTE_DAG_INSTANCE_ID 中取得。 @param total_nodes, 总共的节点个数。正式环境不要填写,将从环境变量 BATCH_COMPUTE_DAG_INSTANCE_COUNT 中取得。 ''' if id==None: id=os.environ['BATCH_COMPUTE_DAG_INSTANCE_ID'] if type(id)==str: id = int(id) if total_nodes==None: total_nodes = os.environ['BATCH_COMPUTE_DAG_INSTANCE_COUNT'] if type(total_nodes)==str: total_nodes = int(total_nodes) if re.match(r'^(\d+)-(\d+)$',render_frames): # 1-2 # continued frames return _parseContinuedFrames(render_frames, total_nodes, id, return_type) else: # intermittent frames return _parseIntermittentFrames(render_frames, total_nodes, id)

1934890530796658 2020-03-28 20:42:58 0 浏览量 回答数 0

回答

Web应用防火墙的接口调用是向WAF API的服务端地址发送HTTP GET请求,并按照接口说明在请求中加入相应请求参数,调用后系统会返回处理结果。 以下Python示例代码演示了如何添加公共参数和接口请求参数、如何用请求参数构造规范化请求字符串、如何构造StringToSign字符串,以及如何获得OpenAPI服务端地址,最终以Get方式发送HTTP请求获取相应的响应结果。 下载Python示例代码 说明 如果您需要使用以下示例,请替换示例中的公共参数及接口请求参数信息。 定义公共参数 #! /usr/bin/env python -- coding: utf-8 -- import hashlib import urllib import requests import hmac import random import datetime import sys class OpenAPI(object): def init(self, signature_version='1.0', api_url=None, ak=None, sk=None, api_version=None): assert api_url is not None assert ak is not None assert sk is not None assert api_version is not None self.signature_once = 0 self.signature_method = 'HMAC-SHA1' self.signature_version = signature_version self.api_version = api_version self.format = 'json' self.signature_method = 'HMAC-SHA1' self.api_url = api_url self.access_key = ak self.access_secret = sk def __gen_common_params(self, req_type, api_version, access_key, access_secret, http_params): while 1: rand_int = random.randint(10, 999999999) if rand_int != self.signature_once: self.signature_once = rand_int break # 当前步骤中是否含有AccessKey参数 if access_key == None: return None http_params.append(('AccessKeyId', access_key)) http_params.append(('Format', self.format)) http_params.append(('Version', api_version)) timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") http_params.append(('Timestamp', timestamp)) http_params.append(('SignatureMethod', self.signature_method)) http_params.append(('SignatureVersion', self.signature_version)) http_params.append(('SignatureNonce', str(self.signature_once))) # 签名 http_params = self.sign(req_type, http_params, access_secret) return urllib.urlencode(http_params) def get(self, http_params=[], host=None, execute=True): data = self.__gen_common_params('GET', self.api_version, self.access_key, self.access_secret, http_params) api_url = self.api_url if data == None: url = "%s" % (api_url) else: url = "%s/?" % api_url + data print ("URL: %s"%url) if execute is False: return url ret = {} try: if host is not None: response = requests.get(url,headers={'Host':host}, verify=False) else: response = requests.get(url, verify=False) ret['code'] = response.status_code ret['data'] = response.text except Exception as e: ret['data'] = str(e) return ret def __get_data(self, http_params): params = self.__gen_common_params('POST', self.api_version, self.access_key, self.access_secret, http_params) if params == []: data = None else: data = params.replace("+", "%20") data = data.replace("*", "%2A") data = data.replace("%7E", "~") return data def post(self, http_params=[], out_fd=sys.stdout): data = self.__get_data(self.api_version, self.access_key, self.access_secret, http_params) api_url = self.api_url out_fd.write(u"[%s] --> (POST):%s\n%s\n" % (datetime.datetime.now(), api_url, data)) ret = requests.post(api_url, data, verify=False) print (ret.text) return ret def sign(self, http_method, http_params, secret): list_params = sorted(http_params, key=lambda d: d[0]) #print list_params url_encode_str = urllib.urlencode(list_params) #print url_encode_str url_encode_str = url_encode_str.replace("+", "%20") url_encode_str = url_encode_str.replace("*", "%2A") url_encode_str = url_encode_str.replace("%7E", "~") string_to_sign = http_method + "&%2F&" + urllib.quote(url_encode_str) #print string_to_sign hmac_key = str(secret + "&") sign_value = str(hmac.new(hmac_key, string_to_sign, hashlib.sha1).digest().encode('base64').rstrip()) http_params.append(('Signature', sign_value)) return http_params 生成接口调用请求 说明 以下代码示例以调用ModifyWafSwitch接口开启Web应用攻击防护功能为例。 from open_api import OpenAPI class Waf(OpenAPI): def init(self, api_url, ak, sk, api_version, instance_id, region): super(Waf, self).__init__(api_url=api_url, ak=ak, sk=sk, api_version=api_version) self.instance_id = instance_id self.region = region def ModifyWafSwitch(self,domain, instance_id=None, region='cn', service_on=1, execute=True): if instance_id is None: instance_id = self.instance_id if region is None: region = self.region params = [ ('Action', 'ModifyWafSwitch'), ('InstanceId', instance_id), ('Domain', domain), ('Region',region), ('ServiceOn', service_on) ] print (params) return self.get(http_params=params,execute=execute) if name == "main": api_url = "https://wafopenapi.cn-hangzhou.aliyuncs.com" # 填写您账号的AccessKeyId信息 ak = "" # 填写您账号的AccessKeyScecret信息 sk = "" # 填写您WAF的实例ID,您可以通过调用GetPayInfo接口获取InstanceID信息 instance_id = "" # 填写您WAF实例所在地域信息 region = "" api_version = "2018-01-17" t = Waf(api_url=api_url, ak=ak, sk=sk, api_version=api_version, instance_id=instance_id, region=region) print (t.ModifyWafSwitch(domain="", service_on=1)) 发送HTTP GET请求 通过上述代码得到HTTP请求,向WAF API的服务端地址发送该HTTP GET请求。 请求示例 https://wafopenapi.cn-hangzhou.aliyuncs.com/?Action=ModifyWafSwitch&Domain=www.aliyun.com&ServiceOn=1&Region=cn&InstanceId=waf_elasticity-cn-0xldbqtm005&TimeStamp=2018-08-23T12:46:24Z&Format=JSON&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2018-01-17&SignatureVersion=1.0&Signature=CT9X0VtwR86fNWSnsc6v8YGOjuE%3D 获得响应结果 最终收到WAF API服务端返回的响应结果。 返回示例 { "RequestId":"D7861F61-5B61-46CE-A47C-6B19160D5EB0", "Result":{ "Status":2, "WafTaskId":"aliyun.waf.20180712214032277.qmxI9a" } }

保持可爱mmm 2020-03-27 18:42:13 0 浏览量 回答数 0

回答

sudo apt-get install sysstat 看CPU:  pidstat -p PID 1 看内存: pidstat -r -p PID 1 看磁盘: pidstat -d -p PDI 1 为了方便排查,建议只开一个工作进程. 把SQLite文件放到内存上/dev/shm测试可以排除磁盘的影响. <?php header('Content-Type: text/plain; charset=utf-8'); $file = '/dev/shm/app/data.db3'; $ddl = " BEGIN; CREATE TABLE IF NOT EXISTS queue (     id           INTEGER PRIMARY KEY AUTOINCREMENT,     user_id      INTEGER ); COMMIT; "; if(!file_exists($file)) {     $db = new PDO('sqlite:'.$file);     $db->exec($ddl); } else {     $db = new PDO('sqlite:'.$file); } $user_id = time(); $stmt = $db->prepare('INSERT INTO queue(user_id) VALUES(?)'); $stmt->execute(array($user_id)); echo 'rowCount:'.$stmt->rowCount()."\n"; echo 'lastInsertId:'.$db->lastInsertId()."\n"; $stmt = $db->query('SELECT * FROM queue WHERE id = '.$db->lastInsertId()); var_export($stmt->fetchAll(PDO::FETCH_ASSOC)); // sudo mkdir -m 777 /dev/shm/app // php -S 127.0.0.1:8080 -t /www >/dev/null 2>&1 & // ab -c100 -n5000 http://127.0.0.1:8080/ // ab -c100 -n500   http://127.0.0.1:8080/ Ubuntu(i5-3230M)上RPS达到2660,而树莓派(BCM2709)上只有185(如果SQLite不在内存上而在SD卡上RPS会降到30).同样的逻辑,在SD卡上比内存/dev/shm上慢了6倍,这时就是磁盘I/O读写的瓶颈.跟i5-3230M比,BCM2709慢了十几倍,这时就是CPU的性能瓶颈了. ######回复 @eechen : 不是什么东西都能粗略的呀,你这代码,真要拿来用的时候,那多出来的数据会加入你后续排序和计算欧几里得距离的过程,影响性能的呀。而且这种影响是毫无意义的浪费,要是写php都像你这么浪费,谁还敢用php. --via 张亦俊######怎么感觉整个osc都把你当猴看,做猴耍一样,哈哈[79] via 金三胖######回复 @Fover : 树莓派跑python还带框架,会比较慢######@乌龟壳 嗯,我知道,只是现在在学习阶段,用树莓派来试试。######@Fover######用gunicorn -w 4,四核四个进程,理论上能用所有的核了。至于为什么不能用100%,可能哪里出现瓶颈了,比如io,或django本身就比较慢;我觉得arm的cpu性能比较慢,可能不上gevent还快点,可以试试不用gevent,只用gunicorn试试,或换一个wsgi服务器,比如bjoern试试######bjoern 晚上回去试试。昨天最后替换了gevent改为meinheld 性能有所提升,但是幅度并不大,晚上回去试试eechen的方法,将数据库挂载到内存中看看。######并不是CPU占用最大,性能才最高啊。######因为cpu利用率只有50%左右,所以我认为还有提升空间的。

kun坤 2020-05-31 13:06:54 0 浏览量 回答数 0

问题

wttr.in 一个 Python 实现的命令行查看天气工具

huc_逆天 2020-05-21 19:12:17 19 浏览量 回答数 1

回答

在这个问题中,我们集中讨论根据特殊语法去解析文本的问题。 为了这样做,你首先要以BNF或者EBNF形式指定一个标准语法。 比如,一个简单数学表达式语法可能像下面这样: expr ::= expr + term | expr - term | term term ::= term * factor | term / factor | factor factor ::= ( expr ) | NUM 或者,以EBNF形式: expr ::= term { (+|-) term }* term ::= factor { (*|/) factor }* factor ::= ( expr ) | NUM 在EBNF中,被包含在 {...}* 中的规则是可选的。*代表0次或多次重复(跟正则表达式中意义是一样的)。 现在,如果你对BNF的工作机制还不是很明白的话,就把它当做是一组左右符号可相互替换的规则。 一般来讲,解析的原理就是你利用BNF完成多个替换和扩展以匹配输入文本和语法规则。 为了演示,假设你正在解析形如 3 + 4 * 5 的表达式。 这个表达式先要通过使用2.18节中介绍的技术分解为一组令牌流。 结果可能是像下列这样的令牌序列: NUM + NUM * NUM 在此基础上, 解析动作会试着去通过替换操作匹配语法到输入令牌: expr expr ::= term { (+|-) term }* expr ::= factor { (*|/) factor }* { (+|-) term }* expr ::= NUM { (*|/) factor }* { (+|-) term }* expr ::= NUM { (+|-) term }* expr ::= NUM + term { (+|-) term }* expr ::= NUM + factor { (*|/) factor }* { (+|-) term }* expr ::= NUM + NUM { (*|/) factor}* { (+|-) term }* expr ::= NUM + NUM * factor { (*|/) factor }* { (+|-) term }* expr ::= NUM + NUM * NUM { (*|/) factor }* { (+|-) term }* expr ::= NUM + NUM * NUM { (+|-) term }* expr ::= NUM + NUM * NUM 下面所有的解析步骤可能需要花点时间弄明白,但是它们原理都是查找输入并试着去匹配语法规则。 第一个输入令牌是NUM,因此替换首先会匹配那个部分。 一旦匹配成功,就会进入下一个令牌+,以此类推。 当已经确定不能匹配下一个令牌的时候,右边的部分(比如 { (*/) factor }* )就会被清理掉。 在一个成功的解析中,整个右边部分会完全展开来匹配输入令牌流。 有了前面的知识背景,下面我们举一个简单示例来展示如何构建一个递归下降表达式求值程序: #!/usr/bin/env python # -*- encoding: utf-8 -*- """ Topic: 下降解析器 Desc : """ import re import collections # Token specification NUM = r'(?P<NUM>\d+)' PLUS = r'(?P<PLUS>\+)' MINUS = r'(?P<MINUS>-)' TIMES = r'(?P<TIMES>\*)' DIVIDE = r'(?P<DIVIDE>/)' LPAREN = r'(?P<LPAREN>\()' RPAREN = r'(?P<RPAREN>\))' WS = r'(?P<WS>\s+)' master_pat = re.compile('|'.join([NUM, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN, WS])) # Tokenizer Token = collections.namedtuple('Token', ['type', 'value']) def generate_tokens(text): scanner = master_pat.scanner(text) for m in iter(scanner.match, None): tok = Token(m.lastgroup, m.group()) if tok.type != 'WS': yield tok # Parser class ExpressionEvaluator: ''' Implementation of a recursive descent parser. Each method implements a single grammar rule. Use the ._accept() method to test and accept the current lookahead token. Use the ._expect() method to exactly match and discard the next token on on the input (or raise a SyntaxError if it doesn't match). ''' def parse(self, text): self.tokens = generate_tokens(text) self.tok = None # Last symbol consumed self.nexttok = None # Next symbol tokenized self._advance() # Load first lookahead token return self.expr() def _advance(self): 'Advance one token ahead' self.tok, self.nexttok = self.nexttok, next(self.tokens, None) def _accept(self, toktype): 'Test and consume the next token if it matches toktype' if self.nexttok and self.nexttok.type == toktype: self._advance() return True else: return False def _expect(self, toktype): 'Consume next token if it matches toktype or raise SyntaxError' if not self._accept(toktype): raise SyntaxError('Expected ' + toktype) # Grammar rules follow def expr(self): "expression ::= term { ('+'|'-') term }*" exprval = self.term() while self._accept('PLUS') or self._accept('MINUS'): op = self.tok.type right = self.term() if op == 'PLUS': exprval += right elif op == 'MINUS': exprval -= right return exprval def term(self): "term ::= factor { ('*'|'/') factor }*" termval = self.factor() while self._accept('TIMES') or self._accept('DIVIDE'): op = self.tok.type right = self.factor() if op == 'TIMES': termval *= right elif op == 'DIVIDE': termval /= right return termval def factor(self): "factor ::= NUM | ( expr )" if self._accept('NUM'): return int(self.tok.value) elif self._accept('LPAREN'): exprval = self.expr() self._expect('RPAREN') return exprval else: raise SyntaxError('Expected NUMBER or LPAREN') def descent_parser(): e = ExpressionEvaluator() print(e.parse('2')) print(e.parse('2 + 3')) print(e.parse('2 + 3 * 4')) print(e.parse('2 + (3 + 4) * 5')) # print(e.parse('2 + (3 + * 4)')) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # File "exprparse.py", line 40, in parse # return self.expr() # File "exprparse.py", line 67, in expr # right = self.term() # File "exprparse.py", line 77, in term # termval = self.factor() # File "exprparse.py", line 93, in factor # exprval = self.expr() # File "exprparse.py", line 67, in expr # right = self.term() # File "exprparse.py", line 77, in term # termval = self.factor() # File "exprparse.py", line 97, in factor # raise SyntaxError("Expected NUMBER or LPAREN") # SyntaxError: Expected NUMBER or LPAREN if __name__ == '__main__': descent_parser()

哦哦喔 2020-04-16 19:23:57 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 物联网平台会对每个接口访问请求的发送者进行身份验证,所以无论使用HTTP还是HTTPS协议提交请求,都需要在请求中包含签名(Signature)信息。 签名方法 签名时,您需在控制台 Access Key 管理 页面查看您的阿里云账号的 AccessKeyId 和 AccessKeySecret,然后进行对称加密。其中,AccessKeyId 用于标识访问者身份;AccessKeySecret 是用于加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密。 说明 物联网平台提供了Java、Python、PHP等语言的服务端SDK。使用我们提供的SDK,可以免去签名过程。请参见SDK下载及各SDK的使用说明。 请按照下面的方法对请求进行签名: 构造规范化的请求字符串(Canonicalized Query String)。 排序参数。 按参数名的字典顺序,对请求参数进行排序,包括 公共请求参数(不包括 Signature 参数)和接口的自定义参数。 说明 当使用 GET 方法提交请求时,这些参数就是请求 URI 中的参数部分,即 URI 中?之后由&连接的部分。 对参数名称和参数值进行 URL 编码。 使用UTF-8字符集按照 RFC3986 规则编码请求参数名和参数值。编码规则如下: 字符 A~Z、a~z、0~9 以及字符-、_、.、~不编码。 其它字符编码成%XY的格式,其中XY是字符对应 ASCII 码的 16 进制表示。比如英文的双引号"对应的编码为%22。 扩展的UTF-8字符,编码成%XY%ZA…的格式。 英文空格要编码成%20,而不是加号+。 该编码方式与application/x-www-form-urlencoded MIME格式编码算法相似,但又有所不同。 如果您使用的是Java标准库中的java.net.URLEncoder,可以先用标准库中percentEncode编码,随后将编码后的字符中加号+替换为%20、星号*替换为%2A、%7E替换为波浪号~,即可得到上述规则描述的编码字符串。 private static final String ENCODING = "UTF-8"; private static String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null; } 使用等号=连接编码后的请求参数名和参数值。 使用与号&连接编码后的请求参数。参数排序与步骤 a 的排序一致。 完成后,即获得规范化请求字符串(CanonicalizedQueryString)。 构造签名字符串。 可以使用 percentEncode 处理步骤 1 得到的规范化字符串,构造签名字符串。可参考如下规则: StringToSign= HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。 percentEncode("/") + "&" + //percentEncode("/"):字符(/)UTF-8 编码得到的值,即 %2F。 percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。 计算 HMAC 值。 按照 RFC2104 的定义,使用步骤 2 得到的字符串StringToSign计算签名 HMAC 值。示例使用的是Java Base64编码方法。 Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) ) 说明 计算签名时,使用的 Key 就是您的 AccessKeySecret 并加上一个与号&字符(ASCII:38)。使用的哈希算法是 SHA1。 计算签名值。 按照 Base64 编码规则把步骤 3 中的 HMAC 值编码成字符串,即得到签名值(Signature)。 添加签名。 将得到的签名值作为 Signature 参数,按照 RFC3986 的规则进行 URL 编码后,再添加到请求参数中,即完成对请求签名的过程。 示例1:参数拼接法 以调用Pub接口为例。假设您的AccessKeyId=testid,AccessKeySecret=testsecret。 签名前的请求URL:http://iot.cn-shanghai.aliyuncs.com/?MessageContent=aGVsbG93b3JsZA%3D&Action=Pub&Timestamp=2017-10-02T09%3A39%3A41Z&SignatureVersion=1.0&ServiceCode=iot&Format=XML&Qos=0&SignatureNonce=0715a395-aedf-4a41-bab7-746b43d38d88&Version=2017-04-20&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcdeZ&TopicFullName=%2FproductKey%2Ftestdevice%2Fget 计算得到的待签名字符串StringToSign:GET&%2F&AccessKeyId%3Dtestid%26Action%3DPub%26Format%3DXML%26MessageContent%3DaGVsbG93b3JsZA%253D%26ProductKey%3D12345abcdeZ%26Qos%3D0%26RegionId%3Dcn-shanghai%26ServiceCode%3Diot%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D0715a395-aedf-4a41-bab7-746b43d38d88%26SignatureVersion%3D1.0%26Timestamp%3D2017-10-02T09%253A39%253A41Z%26TopicFullName%3D%252FproductKey%252Ftestdevice%252Fget%26Version%3D2017-04-20 计算签名值。 因为AccessKeySecret=testsecret,用于计算的Key为testsecret&,计算得到的签名值为: Y9eWn4nF8QPh3c4zAFkM/k/u7eA= 将签名作为Signature参数加入到URL请求中,最后得到的URL为:http://iot.cn-shanghai.aliyuncs.com/?MessageContent=aGVsbG93b3JsZA%3D&Action=Pub&Timestamp=2017-10-02T09%3A39%3A41Z&SignatureVersion=1.0&ServiceCode=iot&Format=XML&Qos=0&SignatureNonce=0715a395-aedf-4a41-bab7-746b43d38d88&Version=2017-04-20&AccessKeyId=testid&Signature=Y9eWn4nF8QPh3c4zAFkM%2Fk%2Fu7eA%3D&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcdeZ&TopicFullName=%2FproductKey%2Ftestdevice%2Fget 示例2:JAVA 语言编码法 本示例代码不需要依赖第三方的库包,可以直接使用。签名步骤如下所示。 构造规范化的请求字符串(参数排序及对参数名称和参数值进行URL编码)。public static Map<String, String> splitQueryString(String url) throws URISyntaxException, UnsupportedEncodingException { URI uri = new URI(url); String query = uri.getQuery(); final String[] pairs = query.split("&"); TreeMap<String, String> queryMap = new TreeMap<String, String>(); for (String pair : pairs) { final int idx = pair.indexOf("="); final String key = idx > 0 ? pair.substring(0, idx) : pair; if (!queryMap.containsKey(key)) { queryMap.put(key, URLDecoder.decode(pair.substring(idx + 1), CHARSET_UTF8)); } } return queryMap; } /** 对参数名称和参数值进行URL编码**/ public static String generate(String method, Map<String, String> parameter, String accessKeySecret) throws Exception { String signString = generateSignString(method, parameter); System.out.println("signString---" + signString); byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString); String signature = newStringByBase64(signBytes); System.out.println("signature---" + signature); if ("POST".equals(method)) return signature; return URLEncoder.encode(signature, "UTF-8"); } 构造待签名的字符串。public static String generateSignString(String httpMethod, Map<String, String> parameter) throws IOException { TreeMap<String, String> sortParameter = new TreeMap<String, String>(); sortParameter.putAll(parameter); String canonicalizedQueryString = UrlUtil.generateQueryString( sortParameter, true); if (null == httpMethod) { throw new RuntimeException("httpMethod can not be empty"); } /** 构造待签名的字符串* */ StringBuilder stringToSign = new StringBuilder(); stringToSign.append(httpMethod).append(SEPARATOR); stringToSign.append(percentEncode("/")).append(SEPARATOR); stringToSign.append(percentEncode(canonicalizedQueryString)); return stringToSign.toString(); } 计算待签名字符串的 HMAC 值。public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception { if (isEmpty(secret)) { throw new IOException("secret can not be empty"); } if (isEmpty(baseString)) { return null; } Mac mac = Mac.getInstance("HmacSHA1"); SecretKeySpec keySpec = new SecretKeySpec( secret.getBytes(CHARSET_UTF8), ALGORITHM); mac.init(keySpec); return mac.doFinal(baseString.getBytes(CHARSET_UTF8)); } private static boolean isEmpty(String str) { return (str == null || str.length() == 0); } 按照 Base64 编码规则编码成字符串,获取签名值。public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException { if (bytes == null || bytes.length == 0) { return null; } return new String(new BASE64Encoder().encode(bytes)); } public static String composeStringToSign(Map<String, String> queries) { String[] sortedKeys = (String[]) queries.keySet() .toArray(new String[0]); Arrays.sort(sortedKeys); StringBuilder canonicalizedQueryString = new StringBuilder(); for (String key : sortedKeys) { canonicalizedQueryString.append("&").append(percentEncode(key)) .append("=") .append(percentEncode((String) queries.get(key))); } StringBuilder stringToSign = new StringBuilder(); stringToSign.append("GET"); stringToSign.append("&"); stringToSign.append(percentEncode("/")); stringToSign.append("&"); stringToSign.append(percentEncode(canonicalizedQueryString.toString() .substring(1))); return stringToSign.toString(); } public static String percentEncode(String value) { try { return value == null ? null : URLEncoder .encode(value, CHARSET_UTF8).replace("+", "%20") .replace("*", "%2A").replace("%7E", "~"); } catch (Exception e) { } return ""; } /** * get SignatureNonce ** */ public static String getUniqueNonce() { UUID uuid = UUID.randomUUID(); return uuid.toString(); } /** * get timestamp **/ public static String getISO8601Time() { Date nowDate = new Date(); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); df.setTimeZone(new SimpleTimeZone(0, "GMT")); return df.format(nowDate); } 添加签名。public static String composeUrl(String endpoint, Map<String, String> queries) throws UnsupportedEncodingException { Map<String, String> mapQueries = queries; StringBuilder urlBuilder = new StringBuilder(""); urlBuilder.append("http"); urlBuilder.append("://").append(endpoint); if (-1 == urlBuilder.indexOf("?")) { urlBuilder.append("/?"); } urlBuilder.append(concatQueryString(mapQueries)); return urlBuilder.toString(); } public static String concatQueryString(Map<String, String> parameters) throws UnsupportedEncodingException { if (null == parameters) { return null; } StringBuilder urlBuilder = new StringBuilder(""); for (Map.Entry<String, String> entry : parameters.entrySet()) { String key = (String) entry.getKey(); String val = (String) entry.getValue(); urlBuilder.append(encode(key)); if (val != null) { urlBuilder.append("=").append(encode(val)); } urlBuilder.append("&"); } int strIndex = urlBuilder.length(); if (parameters.size() > 0) { urlBuilder.deleteCharAt(strIndex - 1); } return urlBuilder.toString(); } public static String encode(String value) throws UnsupportedEncodingException { return URLEncoder.encode(value, "UTF-8"); } }

2019-12-01 23:11:55 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 物联网平台会对每个接口访问请求的发送者进行身份验证,所以无论使用HTTP还是HTTPS协议提交请求,都需要在请求中包含签名(Signature)信息。 签名方法 签名时,您需在控制台 Access Key 管理 页面查看您的阿里云账号的 AccessKeyId 和 AccessKeySecret,然后进行对称加密。其中,AccessKeyId 用于标识访问者身份;AccessKeySecret 是用于加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密。 说明 物联网平台提供了Java、Python、PHP等语言的服务端SDK。使用我们提供的SDK,可以免去签名过程。请参见SDK下载及各SDK的使用说明。 请按照下面的方法对请求进行签名: 构造规范化的请求字符串(Canonicalized Query String)。 排序参数。 按参数名的字典顺序,对请求参数进行排序,包括 公共请求参数(不包括 Signature 参数)和接口的自定义参数。 说明 当使用 GET 方法提交请求时,这些参数就是请求 URI 中的参数部分,即 URI 中?之后由&连接的部分。 对参数名称和参数值进行 URL 编码。 使用UTF-8字符集按照 RFC3986 规则编码请求参数名和参数值。编码规则如下: 字符 A~Z、a~z、0~9 以及字符-、_、.、~不编码。 其它字符编码成%XY的格式,其中XY是字符对应 ASCII 码的 16 进制表示。比如英文的双引号"对应的编码为%22。 扩展的UTF-8字符,编码成%XY%ZA…的格式。 英文空格要编码成%20,而不是加号+。 该编码方式与application/x-www-form-urlencoded MIME格式编码算法相似,但又有所不同。 如果您使用的是Java标准库中的java.net.URLEncoder,可以先用标准库中percentEncode编码,随后将编码后的字符中加号+替换为%20、星号*替换为%2A、%7E替换为波浪号~,即可得到上述规则描述的编码字符串。 private static final String ENCODING = "UTF-8"; private static String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null; } 使用等号=连接编码后的请求参数名和参数值。 使用与号&连接编码后的请求参数。参数排序与步骤 a 的排序一致。 完成后,即获得规范化请求字符串(CanonicalizedQueryString)。 构造签名字符串。 可以使用 percentEncode 处理步骤 1 得到的规范化字符串,构造签名字符串。可参考如下规则: StringToSign= HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。 percentEncode("/") + "&" + //percentEncode("/"):字符(/)UTF-8 编码得到的值,即 %2F。 percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。 计算 HMAC 值。 按照 RFC2104 的定义,使用步骤 2 得到的字符串StringToSign计算签名 HMAC 值。示例使用的是Java Base64编码方法。 Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) ) 说明 计算签名时,使用的 Key 就是您的 AccessKeySecret 并加上一个与号&字符(ASCII:38)。使用的哈希算法是 SHA1。 计算签名值。 按照 Base64 编码规则把步骤 3 中的 HMAC 值编码成字符串,即得到签名值(Signature)。 添加签名。 将得到的签名值作为 Signature 参数,按照 RFC3986 的规则进行 URL 编码后,再添加到请求参数中,即完成对请求签名的过程。 示例1:参数拼接法 以调用Pub接口为例。假设您的AccessKeyId=testid,AccessKeySecret=testsecret。 签名前的请求URL:http://iot.cn-shanghai.aliyuncs.com/?MessageContent=aGVsbG93b3JsZA%3D&Action=Pub&Timestamp=2017-10-02T09%3A39%3A41Z&SignatureVersion=1.0&ServiceCode=iot&Format=XML&Qos=0&SignatureNonce=0715a395-aedf-4a41-bab7-746b43d38d88&Version=2017-04-20&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcdeZ&TopicFullName=%2FproductKey%2Ftestdevice%2Fget 计算得到的待签名字符串StringToSign:GET&%2F&AccessKeyId%3Dtestid%26Action%3DPub%26Format%3DXML%26MessageContent%3DaGVsbG93b3JsZA%253D%26ProductKey%3D12345abcdeZ%26Qos%3D0%26RegionId%3Dcn-shanghai%26ServiceCode%3Diot%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D0715a395-aedf-4a41-bab7-746b43d38d88%26SignatureVersion%3D1.0%26Timestamp%3D2017-10-02T09%253A39%253A41Z%26TopicFullName%3D%252FproductKey%252Ftestdevice%252Fget%26Version%3D2017-04-20 计算签名值。 因为AccessKeySecret=testsecret,用于计算的Key为testsecret&,计算得到的签名值为: Y9eWn4nF8QPh3c4zAFkM/k/u7eA= 将签名作为Signature参数加入到URL请求中,最后得到的URL为:http://iot.cn-shanghai.aliyuncs.com/?MessageContent=aGVsbG93b3JsZA%3D&Action=Pub&Timestamp=2017-10-02T09%3A39%3A41Z&SignatureVersion=1.0&ServiceCode=iot&Format=XML&Qos=0&SignatureNonce=0715a395-aedf-4a41-bab7-746b43d38d88&Version=2017-04-20&AccessKeyId=testid&Signature=Y9eWn4nF8QPh3c4zAFkM%2Fk%2Fu7eA%3D&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcdeZ&TopicFullName=%2FproductKey%2Ftestdevice%2Fget 示例2:JAVA 语言编码法 本示例代码不需要依赖第三方的库包,可以直接使用。签名步骤如下所示。 构造规范化的请求字符串(参数排序及对参数名称和参数值进行URL编码)。public static Map<String, String> splitQueryString(String url) throws URISyntaxException, UnsupportedEncodingException { URI uri = new URI(url); String query = uri.getQuery(); final String[] pairs = query.split("&"); TreeMap<String, String> queryMap = new TreeMap<String, String>(); for (String pair : pairs) { final int idx = pair.indexOf("="); final String key = idx > 0 ? pair.substring(0, idx) : pair; if (!queryMap.containsKey(key)) { queryMap.put(key, URLDecoder.decode(pair.substring(idx + 1), CHARSET_UTF8)); } } return queryMap; } /** 对参数名称和参数值进行URL编码**/ public static String generate(String method, Map<String, String> parameter, String accessKeySecret) throws Exception { String signString = generateSignString(method, parameter); System.out.println("signString---" + signString); byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString); String signature = newStringByBase64(signBytes); System.out.println("signature---" + signature); if ("POST".equals(method)) return signature; return URLEncoder.encode(signature, "UTF-8"); } 构造待签名的字符串。public static String generateSignString(String httpMethod, Map<String, String> parameter) throws IOException { TreeMap<String, String> sortParameter = new TreeMap<String, String>(); sortParameter.putAll(parameter); String canonicalizedQueryString = UrlUtil.generateQueryString( sortParameter, true); if (null == httpMethod) { throw new RuntimeException("httpMethod can not be empty"); } /** 构造待签名的字符串* */ StringBuilder stringToSign = new StringBuilder(); stringToSign.append(httpMethod).append(SEPARATOR); stringToSign.append(percentEncode("/")).append(SEPARATOR); stringToSign.append(percentEncode(canonicalizedQueryString)); return stringToSign.toString(); } 计算待签名字符串的 HMAC 值。public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception { if (isEmpty(secret)) { throw new IOException("secret can not be empty"); } if (isEmpty(baseString)) { return null; } Mac mac = Mac.getInstance("HmacSHA1"); SecretKeySpec keySpec = new SecretKeySpec( secret.getBytes(CHARSET_UTF8), ALGORITHM); mac.init(keySpec); return mac.doFinal(baseString.getBytes(CHARSET_UTF8)); } private static boolean isEmpty(String str) { return (str == null || str.length() == 0); } 按照 Base64 编码规则编码成字符串,获取签名值。public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException { if (bytes == null || bytes.length == 0) { return null; } return new String(new BASE64Encoder().encode(bytes)); } public static String composeStringToSign(Map<String, String> queries) { String[] sortedKeys = (String[]) queries.keySet() .toArray(new String[0]); Arrays.sort(sortedKeys); StringBuilder canonicalizedQueryString = new StringBuilder(); for (String key : sortedKeys) { canonicalizedQueryString.append("&").append(percentEncode(key)) .append("=") .append(percentEncode((String) queries.get(key))); } StringBuilder stringToSign = new StringBuilder(); stringToSign.append("GET"); stringToSign.append("&"); stringToSign.append(percentEncode("/")); stringToSign.append("&"); stringToSign.append(percentEncode(canonicalizedQueryString.toString() .substring(1))); return stringToSign.toString(); } public static String percentEncode(String value) { try { return value == null ? null : URLEncoder .encode(value, CHARSET_UTF8).replace("+", "%20") .replace("*", "%2A").replace("%7E", "~"); } catch (Exception e) { } return ""; } /** * get SignatureNonce ** */ public static String getUniqueNonce() { UUID uuid = UUID.randomUUID(); return uuid.toString(); } /** * get timestamp **/ public static String getISO8601Time() { Date nowDate = new Date(); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); df.setTimeZone(new SimpleTimeZone(0, "GMT")); return df.format(nowDate); } 添加签名。public static String composeUrl(String endpoint, Map<String, String> queries) throws UnsupportedEncodingException { Map<String, String> mapQueries = queries; StringBuilder urlBuilder = new StringBuilder(""); urlBuilder.append("http"); urlBuilder.append("://").append(endpoint); if (-1 == urlBuilder.indexOf("?")) { urlBuilder.append("/?"); } urlBuilder.append(concatQueryString(mapQueries)); return urlBuilder.toString(); } public static String concatQueryString(Map<String, String> parameters) throws UnsupportedEncodingException { if (null == parameters) { return null; } StringBuilder urlBuilder = new StringBuilder(""); for (Map.Entry<String, String> entry : parameters.entrySet()) { String key = (String) entry.getKey(); String val = (String) entry.getValue(); urlBuilder.append(encode(key)); if (val != null) { urlBuilder.append("=").append(encode(val)); } urlBuilder.append("&"); } int strIndex = urlBuilder.length(); if (parameters.size() > 0) { urlBuilder.deleteCharAt(strIndex - 1); } return urlBuilder.toString(); } public static String encode(String value) throws UnsupportedEncodingException { return URLEncoder.encode(value, "UTF-8"); } }

2019-12-01 23:11:56 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 云栖号物联网 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 云栖号弹性计算 阿里云云栖号 云栖号案例 云栖号直播