Les chaînes de caractères placées dans GCS avec python sont déformées lorsqu'elles sont visualisées avec un navigateur

tl;dr

--GCS peut spécifier le type de contenu --Chrome essaie d'afficher text / plain dans Shift-JIS --text / plain; charset = utf-8 est gentil

phénomène

Créez un objet dans Google Cloud Storage avec un script similaire au suivant. Il s'agit d'une version modifiée du code googleapis / python-storage Example Usage pour stocker des chaînes contenant du japonais. Je vais sauter la création du bucket et l'authentification car elles ne sont pas pertinentes ici.

from google.cloud import storage
client = storage.Client()
bucket = client.get_bucket('bucket-id-here')
blob = bucket.get_blob('remote/path/to/file.json')
blob.upload_from_string('{"name": "Japonais"}')

Après avoir exécuté le script, je voudrais vérifier avec le navigateur si l'objet a été créé. スクリーンショット 2020-04-25 1.05.50.png

L'objet semble avoir été créé avec succès. Jetons un coup d'œil au contenu. スクリーンショット 2020-04-25 2.23.36.png

Google Cloud Storage peut générer des liens temporaires et vous pouvez télécharger des objets en suivant le lien depuis votre navigateur.

スクリーンショット 2020-04-25 1.07.30.png

Le contenu de l'objet a été déformé comme ceci. C'est le problème cette fois.

file.json


{"name": "譌 ・ 譛 ャ 隱."}

Enquête

encoder l'hypothèse d'oubli

J'oublie souvent d'encoder et de décoder des chaînes. ʻUpload_from_string` reçoit le type str, mais vérifiez s'il n'était pas nécessaire de l'encoder en UTF-8 etc.

En regardant Code, ce que fait ʻupload_from_string` est simple.

Extrait


def upload_from_string(réduction):
    data = _to_bytes(data, encoding="utf-8")
    string_buffer = BytesIO(data)
    self.upload_from_file(réduction)
  1. Faites des données une chaîne d'octets UTF-8 2.Faites traiter la chaîne d'octets précédente comme un fichier
  2. Appelez ʻupload_from_file`

D'après ce qui précède, l'encodage de la chaîne semble correct.

Hypothèse de type de contenu

Au fait, lorsque je regardais les informations sur l'objet dans le navigateur, j'ai trouvé une partie qui m'intéressait.

スクリーンショット 2020-04-25 2.23.36.png

type="text/plain"

Dans GCS, les métadonnées sont données à l'objet. Il semble que vous puissiez spécifier l'en-tête de réponse lorsque l'objet est appelé dans les métadonnées Content-Type. https://cloud.google.com/storage/docs/metadata#content-type

Par défaut, cela devrait être ʻapplication / octat-stream ou ʻapplication / x-www-form-urlencoded, mais il semble que ce soit text / plain. Est-ce la cause?

Expérience

Puisque j'ai émis l'hypothèse que la cause des caractères déformés est Content-Type: text / plain, je vais mettre en place un serveur à portée de main et vérifier l'affichage afin de le séparer de GCS. Configurez un serveur qui renvoie simplement une chaîne avec Content-Type: text / plain avec bouteille.

server.py


from bottle import Bottle, HTTPResponse
import os

app = Bottle()

@app.route('/')
def serve():
    r = HTTPResponse(status=200, body='Hoge')
    r.set_header('Content-Type', 'text/plain')
    return r

if __name__ == '__main__':
    port = os.environ['PORT'] if 'PORT' in os.environ else '3000'
    app.run(host='0.0.0.0', port=port)

Ouvrir dans le navigateur

スクリーンショット 2020-04-25 2.28.16.png

Je l'ai reproduit.

Ensuite, ouvrez-le dans le navigateur avec ʻapplication / json`.

スクリーンショット 2020-04-25 2.34.56.png

ʻApplication / json` s'affiche correctement.

D'après ce qui précède, il semble bon de penser que Content-Type: text / plain est la cause de caractères déformés indépendamment de GCS.

Pourquoi Content-Type: text / plain

La question demeure de savoir pourquoi «Content-Type», qui devrait être «application / octat-stream» ou «application / x-www-form-urlencoded» par défaut dans GCS, est maintenant «text / plain». Il s'agit du Blob.upload_from_string du module google.cloud.storage utilisé pour le téléchargement. # L1650-L1660) fait quelque chose de mal

Extrait


    def upload_from_string(
        self,
        data,
        content_type="text/plain",
        client=None,
        predefined_acl=None,
        if_generation_match=None,
        if_generation_not_match=None,
        if_metageneration_match=None,
        if_metageneration_not_match=None,
    ):

Puisque l'argument par défaut de content_type est text / plain, il est implémenté pour être Content-Type: text / plain sauf indication contraire.

Comportement du navigateur

Dans le passé, il semble que le spectateur pouvait modifier le codage des caractères, mais maintenant, il semble que l'inférence automatique du navigateur uniquement.

> document.characterSet
"Shift_JIS"

J'essaye d'afficher avec Shift_JIS

L'ampleur du problème

Jusqu'à présent, nous avons constaté que les deux points suivants sont à l'origine de caractères déformés. --L'en-tête de réponse lorsque GCS renvoie un objet est Content-Type: text / plain

Les caractères sont déformés lorsqu'ils sont affichés avec un navigateur, mais ce n'est pas un problème lors du traitement avec un programme comme suit.

from google.cloud import storage
client = storage.Client()
bucket = client.get_bucket('bucket-id-here')
blob = bucket.get_blob('remote/path/to/file.txt')
print(blob.download_as_string())

Dans mon cas, l'objet enregistré est quand même lu par un programme, donc c'était en fait un problème lorsque je voulais voir le contenu facilement. Cela peut poser un problème si vous l'utilisez comme hébergement de fichiers statiques.

Solution

Lorsque vous utilisez upload_from_string, il est bon de spécifier Content-type. Pour json, vous pouvez utiliser ʻapplication / json, et pour le texte, vous pouvez spécifier un jeu de caractères comme text / plain; charset = utf-8`, et Chrome le lira avec utf-8.

Résumé

Je n'ai pas fait attention car cela change rarement récemment. Il n'y a pas beaucoup de leçons apprises cette fois, alors soyez prudent lorsque vous utilisez Blob.upload_from_string.

Recommended Posts

Les chaînes de caractères placées dans GCS avec python sont déformées lorsqu'elles sont visualisées avec un navigateur
Encodage de caractères lors du traitement de fichiers en Python 3
[Débutant] Extraire des chaînes de caractères avec Python
Lors de l'écriture d'un programme en Python
Livre en spirale en Python! Python avec un livre en spirale! (Chapitre 14 ~)
Précautions lors du décapage d'une fonction en python
J'ai fait un compteur de caractères avec Python
Afficher Python 3 dans le navigateur avec MAMP
Afficher pyopengl dans le navigateur (python + anguille)
Erreur lors de l'installation d'un module avec Python pip
[Python] Récupérez les fichiers dans le dossier avec Python
Lire un fichier contenant des lignes brouillées en Python
Créer un environnement virtuel avec conda avec Python
Précautions lors du traitement des structures de contrôle dans Python 2.6
Un mémo lors de la création d'un environnement python avec miniconda
Travaillez dans un environnement virtuel avec Python virtualenv.
Créer une nouvelle page en confluence avec Python
Qu'utilisez-vous lorsque vous testez avec Python?
Prise en compte du moment où vous pouvez faire du bon travail en 10 ans avec Python3 et Scala3.
Une note lors de la vérification si la clé spécifiée existe dans le dictionnaire défini avec python
Je suis resté bloqué en essayant de spécifier un chemin relatif avec relative_to () en python
[Python] Ne laissez que les éléments commençant par une chaîne de caractères spécifique dans le tableau
Comment convertir / restaurer une chaîne avec [] en python
Jouer avec l'API d'intelligence artificielle locale de l'utilisateur en Python
Créez un Slackbot simple avec un bouton interactif en python
[Python] Comment développer des variables dans une chaîne de caractères
Essayez de rechercher un profil d'un million de caractères en Python
Précautions lors du traitement du type ROS MultiArray en Python
Essayez d'incorporer Python dans un programme C ++ avec pybind11
Sélection de la boîte aux lettres lors de la récupération de Gmail avec imaplib de python
Problèmes lors de la création d'un outil de conversion csv-json avec python
Je veux travailler avec un robot en python.
Le point addictif du "raisonnement de Bayes expérimenté en Python"
Utilisez communiquer () lors de la réception de la sortie dans un sous-processus Python
Sortie japonaise lors de l'utilisation de python dans Visual Studio
Exécuter un fichier Python avec une importation relative dans PyCharm
Points à garder à l'esprit lors du traitement des chaînes en Python2
Points à garder à l'esprit lors du traitement des chaînes en Python 3
Pour ceux qui ont des problèmes car NFC est lu à l'infini lors de la lecture de NFC avec Python
Une histoire qui a disparu quand j'ai spécifié un chemin commençant par tilda (~) en python open
Apprentissage automatique Une histoire sur des personnes qui ne sont pas familiarisées avec GBDT utilisant GBDT en Python
Comparer des chaînes en Python
Inverser les chaînes en Python
[Python3] Soyez prudent avec le décapage (strip, lstrip, rstrip)
Essayez d'exécuter python dans l'environnement Django créé avec pipenv
J'ai fait un jeu de frappe simple avec tkinter de Python
Afficher les chaînes de caractères sans saut de ligne en python (mémo personnel)
Un programme polyvalent qui formate les chaînes de commande Linux avec python
[Python] Comment créer une liste de chaînes de caractères caractère par caractère
Publication d'une bibliothèque qui masque les données de caractères dans les images Python
[Petite histoire] [Python] Remplacez les chaînes de caractères dans un tableau à deux dimensions par des nombres
Créer un compte enfant de connect with Stripe en Python
Créons un script qui s'enregistre avec Ideone.com en Python.
Comportement en donnant une liste avec shell = True dans le sous-processus
Un mémo lorsque le visage est détecté avec Python + OpenCV rapidement
[python] Remarques lors de la tentative d'utilisation de numpy avec Cython
Une note lors de la création d'un graphe dirigé à l'aide de Graphviz en Python
Comportement dans chaque langue lorsque les collouts sont réutilisés avec for
Précautions lors de l'utilisation de Python avec AtCoder
Choses à garder à l'esprit lors de l'utilisation de cgi avec python.