Tumgik
#r.post
kyoshi-lesbians · 2 months
Text
The Blind Bandit | The Bullpen
[video description: an amv of Toph as the Blind Bandit set to The Bullpen by Dessa, focused on her earthbending skills fighting in Earth Rumble VI. the ending sequence shows her leaving her home to join Aang, Katara, and Sokka. end description.]
forget the bull in the china shop / there's a china doll in the bullpen
1K notes · View notes
hungryfictions · 1 year
Text
Tumblr media
serge marshennikov. detail from calm, 2018. oil on canvas.
6K notes · View notes
hyacinthmenace · 3 months
Text
Normalizing giving your aromantic besties chocolate on valentines psuedo-queer-platonically.
515 notes · View notes
rexroads · 2 years
Text
Together let us give me the strength to not buy the cute tiger journal
0 notes
gascon-en-exil · 2 years
Note
Very late but genuine question about subtext because of that one r.post; as an asexual with zero interest in shipping whatsoever, isn't people picking up on subtext mostly related to their preferences/fetishes? Because I've seen a lot of (presumably) straight men try to downplay any and all m/m subtext in 3h/3hw with comments about how they dont 'see it', but wouldn't it be perfectly normal for someone with no interest in m/m relationships to not pick up on/look for for that type of subtext?
It's true that subtext is subjective, and people are naturally inclined to see what personally appeals to them in media. However, as is the case with many subjects how you frame subtext when discussing it can be nearly as important as the points you're making.
For example, because I'm neither a woman nor am I attracted to women it can often be harder for me to pick up on F/F subtext, and as I have no personal interest in F/F content it's not something I'll go out of my way to explore unless I'm asked to or if it's relevant to something else I'm talking about. Even so, I'm not going to deny that those readings exist and that other people may enjoy them and would be able to say more about them than I can. You may have no interest in shipping, but you can understand that other people do and that their enthusiasm doesn't diminish your own feelings on the manner. It's entirely possible for people to consume media differently and still be civil about it.
Where it becomes an issue though is when people use their different tastes to vilify others via their favorite characters or ships, especially when it's framed in unnecessarily moral (or political) terms as so much of current fandom culture often is. This has taken a number of forms in Three Houses/Hopes discourse coming from those presumably straight men on Reddit, in addition to a handful of trans lesbians here on Tumblr, including
the blanket generalization that Edelgard, the Eagles, and CF are more progressive than their counterparts (especially the Lions/AM) and naturally appeal to queer people,
relatedly, the refusal to even acknowledge that queer readings of Dimitri exist despite their being numerous and reasonably popular in fandom, or that these readings are only the wishful thinking of "fujoshi",
the erasure of Edelgard's ability to S rank m!Byleth or her romantic endings with other men, along with the dismissal of the role that the (straight) male gaze plays in her presentation in Houses and - as I'm thinking I may have to bring up in my next video - Hopes as well
With statements like these it's no longer a matter of different tastes, but part of a larger and unending war to prove that one's preferred house/route/leader is the most progressive and therefore "best." M/M subtext just happens to be a particular sore point on that front, as the Faerghus boys enjoy the lion's share (pun intended) of that sort of content. Similar to Dimitri's struggles with mental illness, if you so much as acknowledge that Dimitri and AM have queer fans who've picked up on the abundance of subtext among the Lions, then the fan wank soap box falls apart...leaving only the reality that some people like different characters and ships for different reasons.
And then there'd be nothing left to argue over - and that would be terrible.
20 notes · View notes
rnirae · 7 years
Photo
Tumblr media Tumblr media
ig: @ctrl.st
2K notes · View notes
catching-mirage · 7 years
Text
when it’s Eren and Mikasa’s first time having sex in a fic and she rides his dick like a pro and they end up doing it several times in the same night 
Tumblr media
4 notes · View notes
zamolodchikcvas · 5 years
Video
I watched every episode of Instinct twice in two days and this is all I have to show for it. 
151 notes · View notes
loveuhobs · 7 years
Photo
Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media
just a moodboard of my fav pics 💫
180 notes · View notes
Photo
Tumblr media
Misty Engbertsdijksvenen by R.Post http://ift.tt/2iQcAEP
1 note · View note
kyoshi-lesbians · 12 days
Text
Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media
[ID: A series of GIFs showing the parallels between Azula introducing herself to King Keui in Kyoshi Warrior disguise and Ozai asking Fire Lord Azulon to make him the crown prince over Iroh. The last GIF shows Azula and Zuko peeking through curtains watching Ozai speak to Azulon.]
“I am your humble servant, here to serve you and our nation. Use me.” // "We are the Earth King's humble servants."
239 notes · View notes
hungryfictions · 1 year
Text
Tumblr media
serge marshennikov. the dress, 2019. oil on linen.
1K notes · View notes
hyacinthmenace · 3 months
Text
Hermitcraft Season 10 big finale event prediction:Grian gets his first mending book from fishing.
104 notes · View notes
rexroads · 4 years
Text
if I had other social media and we were not in the middle of a pandemic I may well have left this website five minutes ago, when tumblr turned on endless scrolling and hid the setting to turn it back off.
1 note · View note
Photo
Tumblr media
Itsy bitsy spider web by R.Post on Flickr.
5 notes · View notes
masaa-ma · 5 years
Text
Vue.js + Go言語 + Docker で作る!画像アップロード機能実装ハンズオン
from https://qiita.com/po3rin/items/c70105f684e6816621d2?utm_campaign=popular_items&utm_medium=feed&utm_source=popular_items
��んにちはpo3rinです。Vue.js Advent Calender 2018 9日目の記事です。 8日目の記事は vue.js(nuxt.js) の plugin はとても便利 でした。
11月にフリーの案件で Vue.js + Go言語で画像アップロード機能のあるCMSを作りました。Vue.jsでの実装の際には npmモジュールである vue2-dropzone を使うと、Vue.js にとって便利な機能が提供されており、すぐにアップロード機能が作れました。なので今回は Vue.js + Go言語 で画像アップロードを行う機能の実装をハンズオン形式で紹介していきます。
今回は Vue.js のアドベントカレンダーとしての投稿なので、Go言語の実装を飛ばしたい方向けに、Go言語のインストールが不要になるように、すでにDocker環境を用意してあります。せっかくなので今回は Docker を使った 開発環境構築も紹介します。
Go言語の実装を飛ばしたい方は、下記のリポジトリから server ディレクトリをローカルに置いておけば大丈夫です。
今回の実装の Github リポジトリはこちら! https://github.com/po3rin/vue-go-image-uploader
vue2-dropzone の概要
Dropzone.js を使用したファイルアップロード用のVueコンポーネントです。Vue.jsに特化されており、めちゃくちゃ便利です。今回はこの vue2-dropzone の機能を数多く使うので、こちらのドキュメントを参照しながらの実装をお勧めします。 https://rowanwins.github.io/vue-dropzone/docs/dist/#/installation
今回の目指す形
構成は単純です。Dockerコンテナで Vue.js でクライアント、Go言語でAPIサーバーを実装します。
下のように Vue.js + Go言語で画像をアップロードを作ってみます。
当然削除やリスト機能も付けます。
開発用環境構築
今回は Docker を使った 開発環境構築を紹介します。開発環境をDockerで作れば、他のメンバーもすぐに同じ環境を再現できる & グローバルが汚れにくい & 本番環境も立てやすくなるので便利です。一方で Docker の準備がめんどくさい & 自分で準備可能な場合はこのセッションは飛ばしてもらっても大丈夫です。https://qiita.com/po3rin/items/c70105f684e6816621d2#vue2-dropzone-で-画像アップロードを作ってみる
├── client # Vue.js によるクライアント ├── server # Go言語による APIサーバー └── docker-compose.yml # docker-compose 設定ファイル
Vue.js の 開発用 Docker 環境
Vue.jsの開発環境をDockerで作れば、他のメンバーもすぐに同じ環境を再現できる & グローバルが汚れにくい & 本番環境も立てやすくなるので便利です。
まずは Vue CLI 3 が使えることを確認します。もし、入れたことがない場合は下記ドキュメントの手順に従います。
Vue CLI 3 - Installation https://cli.vuejs.org/guide/installation.html
Vue.js のプロジェクトの雛形を作ります。そのまま作られたディレクトリの中で Dockerfile を作りましょう。今回はシンプルなVueで��分なのでpreset は default を洗濯しましょう。
$ vue create client ? Please pick a preset: default (babel, eslint) # <- こちらを選択 $ cd client $ touch Dockerfile
そのままDockerfileに以下の記述を記載します。
# 開発環境 FROM node:10.12-alpine as build-stage WORKDIR /app COPY . . RUN yarn install
コンテナ内に/appディレクトリを作り、 ローカルのclientディレクトリにあるファイルを全てコンテナにコピーし、最後に依存モジュールをインストールします。
これで このイメージにはあとyarn serveするだけでホットリロード入りの開発環境が走る状態まで出来ました。
Go言語の開発用 Docker 環境
(Go言語はパスしたい人や、Vue.jsのセクションだけ学びたい人は、僕の GitHub レポジトリの serverディレクトリだけダウンロードすれば 完成形APIサーバーが動きます。)
client ディレクトリと同じ階層に server ディレクトリを作成します
$ mkdir server $ cd server
今回は Go1.11から導入された vgo + modules を使って環境構築します。vgo が入っていることを確認しましょう。入ってない人は下記の手順に従ってください。
Tour of Versioned Go (vgo) https://research.swtch.com/vgo-tour
$ vgo help Go is a tool for managing Go source code.
そして go.mod と main.go を作ります。
server/main.go を記載します。とりあえずはpingを返すだけで良いでしょう。
main.go
package main // import "server" import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "ping", }) }) r.Run(":8888") }
あとは main.go と同じ階層にDockerfileを作ります。
# 開発環境 FROM golang:1.11 WORKDIR /api COPY . . ENV GO111MODULE=on RUN go get github.com/pilu/fresh CMD ["fresh"]
ここでは github.com/pilu/fresh パッケージでAPIがホットリロードで起動します。ファイルを更新したらそのまま API も更新されます。これで Docker 環境が整いました。
docker-compose で起動してみる
docker-compose.yml を作りましょう。
docker-compose.yml
version: '3' services: client: build: ./client ports: - 8080:8080 volumes: - ./client:/app command: yarn serve server: build: ./server ports: - 8888:8888 volumes: - ./server:/api
これで起動するはずです。ホットリロードはファイルの更新を監視している為、 volumes でローカルのファイルとコンテナ内のファイルを同期させれば、そのままローカルで作業できます。
これで環境が整いました。実際に動くか試してみましょう。
client: localhost:8080 server: localhost:8000
client に関しては実際にブラウザで、server に関しては curlコマンドで確認してみましょう。
$ curl localhost:8888 {"message":"ping"}
これでDockerによる開発環境が構築出来ました。止める際には下記のコマンドを実行しましょう。
vue2-dropzone で 画像アップロードを作ってみる
まずは モジュールである vue2-dropzone をインストールします。ついでにサーバーにリクエストを送るための axios も後で使うので入れておきます。
$ npm install vue2-dropzone axios
これで 画像をアップロードできる便利なコンポーネントを使えます。早速 client/src/components/HelloWorld.vue に vue2-dropzone を追加しましょう。
HelloWorld.vue
<template> <div class="hello"> <vue-dropzone ref="myVueDropzone" id="dropzone" :options="dropzoneOptions"></vue-dropzone> </div> </template> <script> // vue2-dropzone と vue2-dropzone用のcssをimport import vue2Dropzone from 'vue2-dropzone' import 'vue2-dropzone/dist/vue2Dropzone.min.css' export default { name: 'HelloWorld', data: function () { return { dropzoneOptions: { url: `http://localhost/8888/images`, method: 'post' } } }, components: { vueDropzone: vue2Dropzone } } </script> <!-- CSS 省略 -->
<vue-dropzone> コンポーネントは :option で設定を渡せます。今回は dropzoneOptions には アップロード対象となるサーバーのエンドポイント url や アップロード時に使うメソッド method をセッティングしています。すでにファイルをアップロードできるフォームが出来ています。
この他に option に設定できるもののリストはこちらになります。 https://www.dropzonejs.com/#configuration-options
ここまでで画像をドロップもしくは選択できるようになってます。しかしまだアップロード先のサーバーが出来てないので、今から作っていきます。
Go言語でアップロードを受け取るAPIサーバーを作る。
main.go を修正しましょう。
main.go
package main // import "server" import ( "server/handler" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.Use(cors.New(cors.Config{ AllowOrigins: []string{"http://localhost:8080"}, AllowMethods: []string{"GET", "POST", "DELETE", "OPTIONS"}, AllowHeaders: []string{"*"}, })) r.POST("/images", handler.Upload) r.Run(":8888") }
Go言語でJSONを扱うのが少し面倒なのでここでフレームワークの gin を使いましょう。GitHubにあるドキュメントが参考になります。 https://github.com/gin-gonic/gin
POST メソッドで http://localhost:8888/images でファイルアップロードを受け付けます。ちなみに github.com/gin-contrib/cors は gin 用のCORS設定パッケージです。今回は Vue から叩くのでこちらも使います。
実際のリクエストを捌く handler.Upload メソッドを作りましょう。handler/handler.go を作ります。
handler/handler.go
package handler import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) // Upload upload files. func Upload(c *gin.Context) { form, _ := c.MultipartForm() files := form.File["file"] for _, file := range files { err := c.SaveUploadedFile(file, "images/"+file.Filename) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) } } c.String(http.StatusOK, gin.H{"message": "success!!"}) }
gin.Context にはアップロードされたfile達を処理するための、メソッドがいくつか生えています。
今回は gin.Context.SaveUploadedFile を使います。
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
指定されたディレクトリにファイルを保存します。その為、server/images ディレクトリを作っておきましょう。
最終的にserver側の構成はこのようになっています。
. └── server ├── Dockerfile ├── go.mod ├── go.sum ├── handler │   └── handler.go ├── images # image保存用 └── main.go
これだけで画像アップロードを受けれます。Vue.js で作ったクライアントから画像をアップロードしてみましょう。
動きました!
ファイルの名前の重複防ぐ
さて、ここまでの実装だと、同じ名前の画像を上げてしまうと、前にあった画像が上書きされてしまいます。そのため重複を防ぐために uuid で管理するようにしましょう。
HelloWorld.vueを編集しましょう。まずは vue-dropzone に v-on ディレクティブで送信直前に発火するイベントを追加しておきます。他のv-onで発火させることのできるイベント一覧はこちらで確認できます。 https://rowanwins.github.io/vue-dropzone/docs/dist/#/events
変更点はコメントアウトで補足しています。
HelloWorld.vue
<template> <div class="hello"> <!-- sendingEventを追加 --> <vue-dropzone ref="myVueDropzone" id="dropzone" :options="dropzoneOptions" v-on:vdropzone-sending="sendingEvent" ></vue-dropzone> </div> </template> <script> // 省略 ... export default { // 省略 ... // methods を追加 formデータとして fileに付けられた任意のuuidを付加 methods: { sendingEvent: function (file, xhr, formData) { formData.append('uuid', file.upload.uuid) } } } </script> <!-- CSS 省略 -->
これで vue-dropzone がファイル別に自動で付けてくれている uuid をサーバーに送れます。これを使ってサーバー側で画像を判別できます。
それでは handler/handler.go も uuid で画像を判別できるように編集しましょう
handler/handler.go
// 省略... func Upload(c *gin.Context) { form, _ := c.MultipartForm() files := form.File["file"] // uuid を所得 uuid := c.PostForm("uuid") for _, file := range files { // ファイル名にuuidを仕込む err := c.SaveUploadedFile(file, "images/"+uuid+".png") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) } } c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) }
client から 貰った uuid を画像名にして保存します。これで重複を防げるようになりました。試してみてください。
画像の削除
やはりエンジニアたるもの、アップロードした画像を削除したくなってきます。 vue2-dropzone では削除に便利な機能も提供してます。サクッと削除機能を追加しましょう。
まずは 削除機能を追加します。dropzone の設定に一行加えます。
HelloWorld.vue
<!-- 省略 --> <script> export default { name: 'HelloWorld', data: function () { return { dropzoneOptions: { url: 'http://localhost:8888/images', method: 'post', addRemoveLinks: 'true' // ここに1行追加 !!! } } }, // 省略... } </script>
これで ホバーしたら削除ボタンが表示されます。
削除ボタンを押すと、form から画像が消えます。しかし、画面上から消えるだけで、サーバーにアップロードした画像は消えていません。削除ボタンを推すと同時にサーバーの画像を削除するリクエストを送れるようにしましょう。
vue2-dropzone では削除ボタンを押した際のイベントも、送信時同様、v-on で登録できます。
HelloWorld.vue
<template> <div class="hello"> <img width="772" alt="vue-image2.png" src="https://qiita-image-store.s3.amazonaws.com/0/186028/f4d30d47-a702-0c35-bdab-42eb312c9cd8.png"> <!-- sendingEventを追加 --> <vue-dropzone ref="myVueDropzone" id="dropzone" :options="dropzoneOptions" v-on:vdropzone-sending="sendingEvent" v-on:vdropzone-removed-file="removeEvent" ></vue-dropzone> </div> </template> <script> // 省略 ... export default { // 省略 ... // methods を追加 formデータとして fileに付けられた任意のuuidを付加 methods: { sendingEvent: function (file, xhr, formData) { formData.append('uuid', file.upload.uuid) }, removeEvent: function (file, error, xhr) { axios.delete(`http://localhost:8888/images/${file.upload.uuid}`).then(res => { console.log(res.data) }).catch(err => { console.error(err) }) } } } </script> <!-- CSS 省略 -->
これで削除時にサーバーにリクエストを送り、画像を削除してもらいます。ではサーバー側で Delete API を作りましょう。
server/main.go を修正します。
main.go
func main() { r := gin.Default() r.Use(cors.New(cors.Config{ AllowOrigins: []string{"http://localhost:8080"}, AllowMethods: []string{"GET", "POST", "DELETE", "OPTIONS"}, AllowHeaders: []string{"*"}, })) r.POST("/images", handler.Upload) // DELETEメソッドを追加 r.DELETE("/images/:uuid", handler.Delete) r.Run(":8888") }
そして handler.go に Delete ハンドラーを追加します。
handler/handler.go
// Delete remove file. func Delete(c *gin.Context) { uuid := c.Param("uuid") err := os.Remove(fmt.Sprintf("images/%s.png", uuid)) if err != nil { fmt.Println(err.Error()) c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("id: %s is deleted!", uuid)}) }
これで削除も完成しました。
画像のリストを表示する
ここまでの実装では1回アップロードしてからページを更新すると、dropzoneから画像情報が消えるので、何がアップロード済みなのか分からなくなります。このセッションでは1度アップロードした画像をいつでも受け取ってdropzoneにアップロード済みファイルの情報を表示できるようにします。
まずは下準備として、静的ファイルがサーバーから配信できるよにしておきましょう。Nginx などを使っても良いですが、今回はGo言語で画像を返すようにします。
main.go を修正します。ginが提供している静的ファイル配信用のパッケージ github.com/gin-gonic/contrib/static を使います。
main.go
package main // import "server" import ( "server/handler" "github.com/gin-contrib/cors" "github.com/gin-gonic/contrib/static" "github.com/gin-gonic/gin" ) func main() { // 省略 ... // 静的ファイル配信を追加!! r.Use(static.Serve("/", static.LocalFile("./images", true))) r.POST("/images", handler.Upload) r.DELETE("/images/:uuid", handler.Delete) r.Run(":8888") }
これで localhost:8888/{uuid}.png でアクセスすればその画像が返ってくるようになります。確認してみてください。
それではクライアントで アクセス時に 画像のURLリストをAPIから所得し、すでにアップロードしていた画像を form にまとめて表示するようにしましょう。HelloWorld.vue に追加します。
HelloWorld.vue
<script> export default { // 省略 ... mounted () { axios.get('http://localhost:8888/images').then(res => { res.data.forEach(res => { // filename 所得 let filename = res.path.replace('http://localhost:8888/', '') // uuid 所得 let id = filename.replace('.png', '') // file オブジェクト作成 var file = {size: res.size, name: filename, type: "image/png", upload: {uuid: id}} // コードから form に画像データをセット this.$refs.myVueDropzone.manuallyAddFile(file, res.path) }) }).catch(err => { console.error(err) }) }, // 省略 ... } </script>
Vue.js の機能である mounted() を使ってインスタンスがマウントされた後にURLのリストを所得し、データを処理をします。
myVueDropzone.manuallyAddFile(file, fileUrl, callback) は myVueDropzone から生えているメソッドです。これにファイル情報とパスを渡すことによってコードからファイルをアップロードできます。file は vue2-dropzone が期待するオブジェクトの形を渡してあげます。
myVueDropzone.manuallyAddFile(file, fileUrl, callback) 以外のメソッドはこちらで確認できます。 https://rowanwins.github.io/vue-dropzone/docs/dist/#/methods
ここまできたら最後は URL とファイルサイズの JSON を返す APIを 作るだけです。 main.goに一行追加しましょう。
main.go
// 省略 ... func main() { // 省略 ... // GETを追加。!! r.GET("/images", handler.List) r.POST("images", handler.Upload) r.DELETE("/images/:uuid", handler.Delete) r.Run(":8888") }
そして handelr.handler.go にコードを追加します。
handler/handler.go
// 省略 ... // File has file's info. type File struct { Path string `json:"path"` Size int64 `json:"size"` } func dirwalk(dir string) (files []File, err error) { err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { path = strings.Replace(path, "images/", "http://localhost:8888/", 1) size := info.Size() f := File{ Path: path, Size: size, } files = append(files, f) return nil }) if err != nil { return } files = files[1:] return } // List return url & size list func List(c *gin.Context) { files, err := dirwalk("./images") if err != nil { fmt.Println(err) c.JSON(http.StatusNotFound, gin.H{"message": err.Error()}) return } c.JSON(http.StatusOK, files) } // 省略 ...
本来ならデータベースでファイル情報を保存しておきたいところですが、複雑になるので今回は Go言語で頑張って URL と Size をファイル情報から所得して返してます。File構造体はURLとファイルサイズを保持します。そして、少し長いですが、dirwalk関数は指定したディレクトリの中のファイルの情報群 []File を返します。これをJSONとして返却します。
動作確認
アップロード、削除、ページ更新してもアップロードした画像が確認できるのを確認しましょう。
動いてます!!!
まとめ
vue2-dropzone を使ってファイルアップロードがスマートに作成できました。他にもいろんな設定ができるので、ドキュメントをご覧ください。 headerの付与や、アップロード最大数等も設定可能です。 https://rowanwins.github.io/vue-dropzone/docs/dist/#/installation
今回はserver/imagesにアップロードされた画像を置きましたが、実戦では APIサーバーの開発に データベース、AWS S3 などを使えば更に開発しやすくなるでしょう。
0 notes