如何将tempfile内容写入Django FileField?处理时遇段错误
我在Django应用里用rasterio处理地理空间图像,想把输出直接存到Model的FileField里。现在用tempfile写入rasterio输出,再通过Model.FileField.save关联到Model实例保存,但触发了段错误。
Model定义
class MaskedLayer(models.Model): name = models.CharField(max_length=250, blank=True) file = models.FileField( upload_to='masked', null=True, max_length=500)
任务代码示例
from celery import shared_task import rasterio import tempfile from app.models import MaskedLayer @shared_task def process_image(): mask_layer_name = 'processed_image.tif' masked_layer = MaskedLayer.objects.create() with rasterio.open('example.tif') as dataset: # 仅为读取/处理图像示例 out_image = dataset.read() with tempfile.NamedTemporaryFile() as tmpfile: tmpfile.write(out_image) with rasterio.open(tmpfile.name) as dataset: masked_layer.file.save(mask_layer_name, File(dataset)) pass
错误信息
Fatal Python error: Segmentation fault
当前线程 0x00007f453b463740 (最近调用优先):
文件 "/usr/local/lib/python3.11/site-packages/django/views/debug.py", 行232,在get_traceback_frame_variables
文件 "/usr/local/lib/python3.11/site-packages/django/views/debug.py", 行544,在get_exception_traceback_frames
文件 "/usr/local/lib/python3.11/site-packages/django/views/debug.py", 行490,在get_traceback_frames
文件 "/usr/local/lib/python3.11/site-packages/django/views/debug.py", 行320,在get_traceback_data
文件 "/usr/local/lib/python3.11/site-packages/django/views/debug.py", 行403,在get_traceback_text
文件 "/usr/local/lib/python3.11/site-packages/django/utils/log.py", 行125,在emit
文件 "/usr/local/lib/python3.11/logging/init.py", 行978,在handle
文件 "/usr/local/lib/python3.11/logging/init.py", 行1706,在callHandlers
文件 "/usr/local/lib/python3.11/logging/init.py", 行1644,在handle
文件 "/usr/local/lib/python3.11/logging/init.py", 行1634,在_log
文件 "/usr/local/lib/python3.11/logging/init.py", 行1518,在error
文件 "/usr/local/lib/python3.11/site-packages/django/utils/log.py", 行241,在log_response
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行143,在response_for_exception
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行57,在inner
文件 "/usr/local/lib/python3.11/site-packages/django/utils/deprecation.py", 行136,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行55,在inner
文件 "/usr/local/lib/python3.11/site-packages/django/utils/deprecation.py", 行136,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行55,在inner
文件 "/usr/local/lib/python3.11/site-packages/django/utils/deprecation.py", 行136,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行55,在inner
文件 "/usr/local/lib/python3.11/site-packages/django/utils/deprecation.py", 行136,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行55,在inner
文件 "/usr/local/lib/python3.11/site-packages/django/utils/deprecation.py", 行136,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行55,在inner
文件 "/usr/local/lib/python3.11/site-packages/django/utils/deprecation.py", 行136,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", 行55,在inner
文件 "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", 行140,在get_response
文件 "/usr/local/lib/python3.11/site-packages/rest_framework/test.py", 行258,在get_response
文件 "/usr/local/lib/python3.11/site-packages/django/test/client.py", 行153,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/test/client.py", 行805,在request
文件 "/usr/local/lib/python3.11/site-packages/rest_framework/test.py", 行238,在request
文件 "/usr/local/lib/python3.11/site-packages/rest_framework/test.py", 行286,在request
文件 "/usr/local/lib/python3.11/site-packages/django/test/client.py", 行541,在generic
文件 "/usr/local/lib/python3.11/site-packages/rest_framework/test.py", 行234,在generic
文件 "/usr/local/lib/python3.11/site-packages/rest_framework/test.py", 行210,在post
文件 "/usr/local/lib/python3.11/site-packages/rest_framework/test.py", 行296,在post
文件 "/usr/src/app/data_management/tests/test_data_management.py", 行18,在test_solar_pot
文件 "/usr/local/lib/python3.11/unittest/case.py", 行579,在_callTestMethod
文件 "/usr/local/lib/python3.11/unittest/case.py", 行623,在run
文件 "/usr/local/lib/python3.11/unittest/case.py", 行678,在__call__
文件 "/usr/local/lib/python3.11/site-packages/django/test/testcases.py", 行416,在_setup_and_call
文件 "/usr/local/lib/python3.11/site-packages/django/test/testcases.py", 行381,在__call__
文件 "/usr/local/lib/python3.11/unittest/suite.py", 行122,在run
文件 "/usr/local/lib/python3.11/unittest/suite.py", 行84,在__call__
文件 "/usr/local/lib/python3.11/unittest/runner.py", 行217,在run
文件 "/usr/local/lib/python3.11/site-packages/django/test/runner.py", 行980,在run_suite
文件 "/usr/local/lib/python3.11/site-packages/django/test/runner.py", 行1058,在run_tests
文件 "/usr/local/lib/python3.11/site-packages/django/core/management/commands/test.py", 行68,在handle
文件 "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", 行448,在execute
文件 "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", 行402,在run_from_argv
文件 "/usr/local/lib/python3.11/site-packages/django/core/management/commands/test.py", 行24,在run_from_argv
文件 "/usr/local/lib/python3.11/site-packages/django/core/management/init.py", 行440,在execute
文件 "/usr/local/lib/python3.11/site-packages/django/core/management/init.py", 行446,在execute_from_command_line
文件 "/usr/src/app/manage.py", 行18,在main
文件 "/usr/src/app/manage.py", 行22,在扩展模块: psycopg2._psycopg, numpy.core._multiarray_umath, numpy.core._multiarray_tests, numpy.linalg._umath_linalg, numpy.fft._pocketfft_internal, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._mt19937, numpy.random.mtrand, numpy.random._philox, numpy.random._pcg64, numpy.random._sfc64, numpy.random._generator, rasterio._version, rasterio._err, rasterio._filepath, rasterio._env, rasterio._transform, rasterio._base, rasterio.crs, rasterio._features, rasterio._warp, rasterio._io, fiona._err, fiona._geometry, fiona._shim, fiona._env, fiona.schema, fiona.ogrext, fiona._crs (总计: 31)
问题原因及修复方案
核心问题
- 直接写入numpy数组到临时文件:
out_image是rasterio读取返回的numpy数组,直接用tmpfile.write(out_image)会把数组的二进制内存数据写入,不是合法的TIFF格式,导致后续rasterio读取时触发底层C扩展的段错误。 - 错误传递rasterio Dataset对象给Django File:Django的
File需要接收标准类文件对象,而rasterio的Dataset不符合该要求,直接传入会引发内存访问错误。
修正后的代码
from celery import shared_task import rasterio import tempfile import os from django.core.files import File from app.models import MaskedLayer @shared_task def process_image(): mask_layer_name = 'processed_image.tif' masked_layer = MaskedLayer.objects.create() # 读取源图像并保留元数据 with rasterio.open('example.tif') as src_dataset: out_image = src_dataset.read() out_meta = src_dataset.meta.copy() # 创建临时文件并通过rasterio写入合法TIFF with tempfile.NamedTemporaryFile(suffix='.tif', delete=False) as tmpfile: tmp_path = tmpfile.name with rasterio.open(tmp_path, 'w', **out_meta) as dst_dataset: dst_dataset.write(out_image) # 将标准文件对象传给Django FileField保存 with open(tmp_path, 'rb') as f: masked_layer.file.save(mask_layer_name, File(f), save=True) # 手动删除临时文件 os.unlink(tmp_path)
关键修改点
- 用rasterio生成合法TIFF:通过rasterio的写入模式打开临时文件,传入源图像的元数据,确保输出是标准TIFF格式。
- 传递标准类文件对象:打开临时文件为二进制读模式,将这个文件对象传给Django的
File,而非rasterio的Dataset。 - 控制临时文件生命周期:添加
delete=False避免文件提前被删除,保存完成后手动清理临时文件。
内容的提问来源于stack exchange,提问作者Nikko

