我能否在Python中将io.BytesIO()流传输到subprocess.popen()?

16

我想使用subprocess.popen()io.BytesIO()的字节流传输到另一个程序,但我不知道如何实现或者是否可能。文档和示例都是关于文本和换行符的。

当我使用以下代码时:

import io
from subprocess import *

stream = io.BytesIO()
someStreamCreatingProcess(stream)

command = ['somecommand', 'some', 'arguments']  
process = Popen(command, stdin=PIPE)
process.communicate(input=stream)

我明白了

Traceback (most recent call last):
  File "./test.py", line 9, in <module>
    procOut         = process.communicate(input=stream)
  File "/usr/lib/python2.7/subprocess.py", line 754, in communicate
    return self._communicate(input)
  File "/usr/lib/python2.7/subprocess.py", line 1322, in _communicate
    stdout, stderr = self._communicate_with_poll(input)
  File "/usr/lib/python2.7/subprocess.py", line 1384, in _communicate_with_poll
    chunk = input[input_offset : input_offset + _PIPE_BUF]
TypeError: '_io.BytesIO' object has no attribute '__getitem__'

我认为popen()仅适用于文本。我的看法对吗?
有其他的方法可以实现这个功能吗?

2个回答

10

正如@falsetru所说,您不能直接流式传输BytesIO()对象;您需要先从中获取一个字节串。

这意味着在调用 stream.getvalue() 以将其传递给 process.communicate() 前,所有内容都应已写入到 stream 中。

如果您想要进行流式传输而不是一次性提供所有输入,则可以放弃使用BytesIO()对象并直接写入管道:

from subprocess import Popen, PIPE

process = Popen(['command', 'arg1'], stdin=PIPE, bufsize=-1)
someStreamCreatingProcess(stream=process.stdin) # many `stream.write()` inside
process.stdin.close() # done (no more input)
process.wait()

someStreamCreatingProcess() 应该等到它写完流后再返回。如果它立即返回,则应在将来某个时间调用stream.close()(从代码中删除process.stdin.close()):

from subprocess import Popen, PIPE

process = Popen(['command', 'arg1'], stdin=PIPE, bufsize=-1)
someStreamCreatingProcess(stream=process.stdin) # many `stream.write()` inside
process.wait() # stream.close() is called in `someStreamCreatingProcess`

点赞。你说的“不应该”是什么意思?一般来说不应该这样做,还是使用此代码后不会这样做?通常someStreamCreatingProcess()会立即返回,但会继续流式传输,直到明确接收到停止流式传输(在此情况下为视频)的命令。 - Redsandro
1
@Redsandro:我已经展示了如何处理someStreamCreatingProcess()立即返回的情况。 - jfs
@Sebastian:谢谢,我回来后会尝试一下。 - Redsandro
@Sebastian:我回来了,我接受了你的答案。 :) - Redsandro

7
根据subprocess.Popen.communicate:
可选的输入参数应该是一个字符串,发送给子进程或者None,如果不需要向子进程发送任何数据。
要从BytesIO对象中获取(字节)字符串值,请使用getvalue:
process.communicate(input=stream.getvalue())

1
尽管此答案解决了错误消息,但生成的管道数据无法被例如 ffmpegpipe:: Invalid data found when processing input. 所接受。当首先将流直接写入文件时,ffmpeg接受 该文件。我认为 @SteveBarnes 也在这种情况下提出了一些相关的建议。 - Redsandro
2
@Redsandro,当您调用subprocess.Popen时,stream是否包含所有数据? - falsetru
1
@SteveBarnes:bufsize 不影响正确性——无论 ffmpeg 是否工作——只影响其时间性能。universal_newlines 默认情况下为 False;您不需要明确指定它。 - jfs
1
@Redsandro,那么J.F. Sebastian的答案就是你想要的。 - falsetru
我发现一些关于某些Python版本默认将universal_newlines设置为True并添加一个不必要的参数的评论,与默认值匹配通常不是问题。 - Steve Barnes
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接