Create and return a CP932 CSV file for Excel with Chalice

Introduction

When creating a management system for Web applications, I often encounter the pattern "I want you to be able to download system data in a CSV file that can be opened in Excel". If you create CSV without thinking about anything in Python3 system, it will be UTF-8 character encoding, so if it is the default setting of Excel, the characters will be garbled. Of course, if you specify the reading encoding properly in Excel, you can open it without problems, but it is often said that "I do not understand that, so do not garble from the beginning". In that case, it is necessary to create a CSV file with the encoding for Windows (CP932, or Shift_JIS, SJIS).

This time I was using Chalice, but as a result of looking closely at the document, I got stuck, so I made a note.

version etc.

$ pipenv run chalice --version
chalice 1.21.2, python 3.8.2, linux 5.4.0-52-generic

Read the documentation

This time I want to return it with Response, so I thought that it would be possible if I put an appropriate value in the body of the Response class. However, the document at the time of writing the article (October 23, 2020) states:

class Response(body, headers=None, status_code=200) body: The HTTP response body to send back. This value must be a string.

Quoted and excerpted from https://aws.github.io/chalice/api.html#response. Partially emphasized.

Thinking when reading this.

"Eh ...? The character string encoded in CP932 is a bytes type, so I can't pass it here ...? If that's not possible, create a file, upload it to S3, and have it downloaded ..."

However, ** body passes even if bytes are entered **. The string in this document is meant to be a wider string than just the str type. When I looked it up again, A person with the same question asked me a question.

Implementation example: Direct response

You can also pass ** bytes type ** to Chalice's Response # body **. If you implement it based on that, it will look like this. If you access / with a browser, it will be downloaded as a CSV file.

app.py


#!/usr/bin/python
# -*- coding: utf-8 -*-

import csv
import tempfile

from chalice import Chalice, Response

app = Chalice(app_name='csvtest')


def csv_response(filename, encoding='utf8'):
    """How to return a CSV file 1:Direct response"""
    with tempfile.TemporaryFile(mode='r+', encoding=encoding) as fh:
        #Write with writer
        writer = csv.writer(fh, lineterminator='\r\n')
        writer.writerow(['username', 'Login date and time'])
        writer.writerow(['user01', '2000/01/01 00:00:00'])
        #Read all the written data into data
        fh.seek(0)
        data = fh.read()
    headers = {}
    headers['Content-Type'] = 'text/csv'
    headers['Content-Disposition'] = f'attachment;filename="{filename}"'
    return Response(body=data, status_code=200, headers=headers)


@app.route('/')
def index():
    return csv_response('test.csv', encoding='cp932')

Alternative: Upload files to S3

In the previous example, tmpfile is used to create a file on-memory and then read data. However, in the case of AWS Lambda, it is executed in an environment where the memory usage is limited, so if the file becomes large, it can be imagined that the memory usage will be significantly affected. Of course, this is a problem that can be avoided by raising the upper limit of memory usage, but if the charge doubles, you may hesitate. In such a case, you can temporarily create a file under / tmp provided by AWS Lambda and upload the created file to S3. After uploading to S3, you can access the file created via the communication of CloudFront --S3, or create and provide a temporary URL to let the user download it.

Note that TempfileContext is not the essence of the code because it is prepared so that the files under / tmp used in AWS Lambda can be deleted without considering error handling.

import os
import csv
import uuid

import boto3

s3 = boto3.client('s3')


class TempfileContext:
    """Provides a context to create and delete temporary files"""
    def __init__(self):
        tmpfile = str(uuid.uuid4())
        self.filename = f'/tmp/{tmpfile}'

    def __enter__(self):
        return self

    def __exit__(self, ex_type, ex_value, trace):
        try:
            if os.path.exists(self.filename):
                os.remove(self.filename)
        except Exception:
            pass


def create_and_upload_csv(filename, encoding='utf8'):
    """How to return a CSV file 2:Create CSV and upload it to S3"""
    with TempfileContext() as tmp:
        # 1. /Create a CSV file in the tmp area
        with open(tmp.filename, 'w', encoding=encoding) as fh:
            #Write with writer
            writer = csv.writer(fh, lineterminator='\n')
            writer.writerow(['username', 'Login date and time'])
            writer.writerow(['user01', '2000/01/01 00:00:00'])

        # 2.Upload to S3
        s3.upload_file(tmp.filename, BUCKETNAME, f'uploads/{filename}')

Recommended Posts

Create and return a CP932 CSV file for Excel with Chalice
Create a temporary file with django as a zip file and return it
Create an Excel file with Python3
Create a file uploader with Django
Create a striped illusion with gamma correction for Python3 and openCV3
Quickly create an excel file with Python #python
Create a large text file with shellscript
Create a VM with a YAML file (KVM)
Create Excel file with Python + similarity matrix
Read and format a csv file mixed with comma tabs with Python pandas
How to read a CSV file with Python 2/3
Create a GUI executable file created with tkinter
Create a LINE BOT with Minette for Python
Create a PDF file with a random page size
Create a dashboard for Network devices with Django!
Draw a graph with matplotlib from a csv file
Create a cylinder with open3d + STL file output
Create a simple reception system with the Python serverless framework Chalice and Twilio
Create a clean DB for testing with FastAPI and unittest the API with pytest
Create a native GUI app with Py2app and Tkinter
Create a batch of images and inflate with ImageDataGenerator
Create a Layer for AWS Lambda Python with Docker
Create a 3D model viewer with PyQt5 and PyQtGraph
Python script to create a JSON file from a CSV file
[Python] Create a Tkinter program distribution file with cx_Freeze
Create a 2d CAD file ".dxf" with python [ezdxf]
[Linux] Create a self-signed certificate with Docker and apache
Create an audio file with the text-to-speech function with Google Text To Speak and check the text as a guide for the speech for 3 minutes.
Parse the Researchmap API in Python and automatically create a Word file for the achievement list
Create a new csv with pandas based on the local csv
[Python] Create a file & folder path specification screen with tkinter
Create a web surveillance camera with Raspberry Pi and OpenCV
[Python] Read the csv file and display the figure with matplotlib
[Python] Create a date and time list for a specified period
Create applications, register data, and share with a single email
Let's create a PRML diagram with Python, Numpy and matplotlib.
Installation procedure for Python and Ansible with a specific version
Stylish technique for pasting CSV data into Excel with Python
Create a deploy script with fabric and cuisine and reuse it
Library for specifying a name server and dig with python
Create a social integration API for smartphone apps with Django
[Python] Create a screen for HTTP status code 403/404/500 with Django
With me, cp, and Subprocess
Read and write csv file
Create a homepage with django
Read and write a file
Create a dummy data file
Create a heatmap with pyqtgraph
Write and read a file
Create a directory with python
Create xlsx file with XlsxWriter
Create a Python execution environment for Windows with VScode + Remote WSL
How to paste a CSV file into an Excel file using Pandas
Let's create a tic-tac-toe AI with Pylearn 2-Save and load models-
Create file update history and brief release notes with PySImple GUI
Create a color picker for the color wheel with Python + Qt (PySide)
How to create a label (mask) for segmentation with labelme (semantic segmentation mask)
Create a private DMP with zero initial cost and zero development with BigQuery
I tried to create Bulls and Cows with a shell program
Create a development environment for Go + MySQL + nginx with Docker (docker-compose)
[For beginners] Read Excel / CSV files into DataFrame with Google Colaboratory