LoginSignup
0
0

More than 5 years have passed since last update.

iPhoneに溜まっている写真をパソコンで直に吸い上げたいっ!

Last updated at Posted at 2016-09-18

背景

私、iPhone使いです。
音楽聞く習慣がないので、パソコンにiTunes入れる必要性を感じません。と云うか入れたくありません。

私、秘密主義者です。
誰に覗かれるか分からないクラウドストレージはイマイチ不安。と云うか全く信用していません。

私、激烈心配性です。
離席中にひょっとして誰かにファイル抜かれたら嫌だなぁ。と云う事でWPDも無効化しちゃいました。


……………………
iPhoneに溜まり続ける写真。
何とかしてパソコンに吸い上げられないかなぁ…

なお
        このドラマは大筋に於いてフィクションであり、
        登場する人物・団体・シチュエーションとかは
        現実世界での出来事とは、ほぼほぼ無関係…
                                  と思って下さい

方針

なんて我儘で贅沢な悩みでしょうか。
こんだけ周りに壁を立てておいて、写真だけは特別扱いしたいなんて。

ま、実際問題としてそんな事って良くありますけどね。
でも、そんな状況に出くわしてしまうと、思わずめらめらと何かが燃え上がってしまう自分がいます。


さて、そんな時取り敢えず思いつくのはやっぱりWebアプリですね。クラウドに向かっては出て行きたくないので、パソコンとiPhone間のプライベートネットワークって感じでしょうか。
となると、万人が「あーこりゃ簡単・便利!」って感じにはなりそうにもありませんが、まぁ「こんな事もできるんだねぇ…」位に思って貰えれば嬉しいかな。

構成

全体的な想定構成を決めておきましょう。
基本的に、動作検証の取れた自前の環境って事ですが…

  • iPhone
  • Windowsパソコン
    OSはWin10
  • 宅内の無線LAN環境が必要です
  • パソコン側にIISが必要ですね
    事前に設定しておきましょう
    ここら辺が参考になるかも

対応

一番オーソドックスな方法です。パソコンをWebサーバに見立てて(?いや、しっかりとWebサーバですけど)、iPhoneからそこにアクセスする感じです。
まず、iPhoneでパソコンにアクセスできるかどうか確認してみましょう。

パソコンのIPアドレスを確認した処、現時点ではWiFiアダプタのIPアドレスは192.168.0.11でした。

image

んじゃ、iPhonesafari立ち上げてhttp://192.168.0.11とすると…

image20160904213159610.png

よしよし。

じゃぁ、画像をアップロードする為に、c:\inetpub\wwwrootの下に以下のファイルを置きましょう。(UTF-8で保存してね)

select.html
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=300">
<title>画像転送</title>
</head>
<body>
<h1>画像転送</h1><br/>
<form action="upload.aspx" method="post" enctype="multipart/form-data">
<input type="file" name="upfile" multiple="multiple"/><br/><br/>
<input type="submit" value="転送"/>
</form>
</body>
</html>
upload.aspx
<%@ Page Language="C#" CodeFile="upload.aspx.cs" Inherits="MyPage" %>
<% Proc(); %>
upload.aspx.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;

public partial class MyPage : System.Web.UI.Page {
    const string imgPath = @"c:\users\■■■■\desktop\upImage";

    protected void Proc() {
        for(int i=0; i<Request.Files.Count; i++) {
            string fileName = Path.GetFileName(Request.Files[i].FileName);
            fileName = Regex.Replace(FileName, @"(?=\.\w+$)|(?<!\.\w+)$", DateTime.Now.ToString("yyyyMMddHHmmssfff"));
            Request.Files[i].SaveAs(Path.Combine(imgPath, fileName));
            Thread.Sleep(1);
        }
        Response.Write(@"<!DOCTYPE HTML>");
        Response.Write(@"<html lang=""ja"">");
        Response.Write(@"<head>");
        Response.Write(@"<meta charset=""UTF-8"">");
        Response.Write(@"<meta name=""viewport"" content=""width=300"">");
        Response.Write(@"<title>転送完了</title>");
        Response.Write(@"</head>");
        Response.Write(@"<body>");
        Response.Write(@"<a href=""select.html""><button>戻る</button></a>");
        Response.Write(@"</body>");
        Response.Write(@"</html>");
    }
}

あ、■■■■の所は自分のユーザ名にして下さい。
そして、デスクトップにupImageフォルダを作って、EveryOneに対してFullControlを与えておく必要があります。ここら辺が、IISのセットアップと共に面倒臭いハードルですかねぇ…

で、safarihttp://192.168.0.11/select.htmlにアクセスすると…

image20160904225759384.png

ファイル選択ボタンを押すと…

image20160904225759392.png

よしよしよし…

画像を選択して、転送ボタンを押すと

image20160904225824981.png

デスクトップのupImageに画像ファイルが転送されました。
よしよしよしよし…

別解

できるのはできたけど、やっぱりIIS立てたり、転送先を絶対パスで固定したり、妙なアクセス権付けたり…
と、イマイチ感がハンパないです。

なんとかなんないのー、どらヱモ~〇っ!


 — という事で、良いもの見つけました。
その名もOWIN

IIS不要!
起動するだけでOK!!

自分の中にWebサーバを抱え込んでくれる、と云う優れモノ。
つまりexeを起動するだけで外部からのWebアクセスを引き受けてくれると云う…


では、始まりー

  • Visual Studio Community 2015(等) を準備して下さい。
  • 新規プロジェクトを始めましょう
    image
    image
  • ↓この画面は、今回Azure使わないので(もし出ても)CancelしちゃってOKです image
  • ↓プロジェクトに新しい項目を追加しましょう image
  • ↓OWIN Startup クラスを追加します image
  • ↓ツール→NuGet、経由でOWINHostを追加しましょう(色々確認されるけど、気にしない気にしない) image
    imageimageimage
  • ↓追加されたStartup1クラスのConfigurationメソッドを整備しましょう
    こんな感じかなっ
    (本当は多分コントローラとかで対応するんだろうけど、今回は自分の為だけなので楽をしてみました)
Startup1.cs
public class Startup1 {
    public void Configuration(IAppBuilder app) {
        // アプリケーションの設定方法の詳細については、http://go.microsoft.com/fwlink/?LinkID=316888 を参照してください
        app.Run(context => {
            StringBuilder sb = new StringBuilder();

            switch(context.Request.Path.Value) {
            case "/":
                context.Response.Redirect("/select");
                break;

            case "/select":
                sb.AppendLine(@"<!DOCTYPE HTML>");
                sb.AppendLine(@"<html lang=""ja"">");
                sb.AppendLine(@"<head>");
                sb.AppendLine(@"<meta charset=""UTF-8"">");
                sb.AppendLine(@"<meta name=""viewport"" content=""width = 300"">");
                sb.AppendLine(@"<title>画像転送</title>");
                sb.AppendLine(@"</head>");
                sb.AppendLine(@"<body>");
                sb.AppendLine(@"<h1>画像転送</h1><br/>");
                sb.AppendLine(@"<form action=""upload"" method=""post"" enctype=""multipart/form-data"">");
                sb.AppendLine(@"<input type=""file"" name=""upfile"" multiple=""multiple""/><br/><br/>");
                sb.AppendLine(@"<input type=""submit"" value=""転送""/>");
                sb.AppendLine(@"</form>");
                sb.AppendLine(@"</body>");
                sb.AppendLine(@"</html>");

                context.Response.Write(sb.ToString());
                break;

            case "/upload":
                for(int i=0; i<context.Request.Files.Count; i++) {
                    string fileName = Path.GetFileName(context.Request.Files[i].FileName),;
                    fileName = Regex.Replace(fileName, @"(?=\.\w+$)|(?<!\.\w+)$", DateTime.Now.ToString("yyyyMMddHHmmssfff"));
                    context.Request.Files[i].SaveAs(Path.Combine(imgPath, fileName));
                    Thread.Sleep(1);
                }

                sb.AppendLine(@"<!DOCTYPE HTML>");
                sb.AppendLine(@"<html lang=""ja"">");
                sb.AppendLine(@"<head>");
                sb.AppendLine(@"<meta charset=""UTF-8"">");
                sb.AppendLine(@"<meta name=""viewport"" content=""width = 300"">");
                sb.AppendLine(@"<title>画像転送</title>");
                sb.AppendLine(@"</head>");
                sb.AppendLine(@"<body>");
                sb.AppendLine(@"<a href=""select""><button>戻る</button></a>");
                sb.AppendLine(@"</body>");
                sb.AppendLine(@"</html>");

                context.Response.Write(sb.ToString());
                break;

            default:
                context.Response.StatusCode = 404;
                break;
            }

            return context.Response.WriteAsync(string.Empty);
        });
    }
}

あ~~~でもぉ、コンパイルが通らないぃ~ orz
OWINRequestにはFilesがなかったのかー


いや、でも何か解決策がある筈!
   :
ある筈…
 :

力技

見つからない…
NancyってのではFilesを扱っている例は見つけたんですが、残念ながらナンシー嬢、私の環境へのお誘いにはつれない感じ…
コンパイルまでも辿り着けない体たらく。

已むを得ん!
この際無理矢理にでもファイルを取り出してやるっ!

って事で、無理矢理感満載の解決を見ました。
HTTPInputStreamから直接ファイルを切り出す方式です。
※ エラーになる事を基本的に想定していない実装なので、とても打たれ弱いです
  今日の所はこれ位で勘弁して下さい…


ざっくりと方向性を説明すると—

  • HttpFileCollection相当のクラスを作る
  • HttpPostedFile相当のクラスを作る
    あれ?
    終わってしまった…

では、まず上記クラスから—

FileCollection.cs
class FileCollection : NameObjectCollectionBase, IDisposable {
    private FileCollection() : base() { }
    public void Dispose() { ((IDisposable)ms).Dispose(); }
    public PostedFile this[int index] { get { return (PostedFile)this.BaseGet(index); } }

    public PostedFile this[string key] {
        get { return (PostedFile)this.BaseGet(key); }
        set { this.BaseSet(key, value); }
    }

    public string[] AllStringValues { get { return (string[])this.BaseGetAllValues(typeof(string)); } }
    public bool HasKeys { get { return this.BaseHasKeys(); } }
    public void Add(string key, PostedFile value) { this.BaseAdd(key, value); }
    public void Remove(string key) { this.BaseRemove(key); }
    public void Remove(int index) { this.BaseRemoveAt(index); }
    public void Clear() { this.BaseClear(); }

    MemoryStream ms = null;

    static public FileCollection GetFiles(IOwinContext context) {
        FileCollection fc = new FileCollection();

        HttpListenerRequest req = ((context.Request.Environment["System.Net.HttpListenerContext"]) as HttpListenerContext).Request;
        string boundary = "--" + Regex.Match(req.ContentType, @"(?<=boundary=)(.+(?=; )|.+$)").Value;

        fc.ms = new MemoryStream((int)req.ContentLength64);
        byte[] work = new byte[1024 * 1024];
        int cnt = 0;
        using(Stream stream = req.InputStream) {
            while((cnt = stream.Read(work, 0, work.Length)) > 0)
                fc.ms.Write(work, 0, cnt);
        }

        fc.ms.Seek(0, SeekOrigin.Begin);

        if(fc.ms.GetString(req.ContentEncoding, boundary.Length) != boundary) return fc;

        while(fc.ms.GetString(req.ContentEncoding, 2) == "\r\n") {
            string filename = null;
            string filetype = null;
            long spos = 0;
            long epos = 0;

            string line = null;
            while((line = fc.ms.GetLine(req.ContentEncoding)) != string.Empty) {
                if(line.StartsWith("Content-Disposition: ")) {
                    filename = Regex.Match(line, @"(?<=filename=)(.+(?=; )|.+$)").Value.Trim('"');
                    continue;
                }
                if(line.StartsWith("Content-Type: ")) {
                    filetype = Regex.Match(line, @"(?<= ).+$").Value;
                    continue;
                }
            }

            spos = fc.ms.Position;
            do {
                epos = fc.ms.SkipLine();
            } while(!fc.ms.IsFileEnd(req.ContentEncoding, boundary));

            fc.Add(Path.GetFileName(filename), new PostedFile(fc.ms, filetype, filename, spos, epos));
        }

        return fc;
    }
}


PostedFile.cs
class PostedFile {
    public PostedFile(Stream baseStream, string type, string name, long start, long end) {
        this.Base = baseStream;
        this.StartPos = start;
        this.ContentLength = (int)(end - start);
        this.ContentType = type;
        this.FileName = name;
    }

    Stream Base = null;

    private long StartPos = 0;
    public int ContentLength { get; private set; }
    public string ContentType { get; private set; }
    public string FileName { get; private set; }
    public Stream InputStream { get { return new SubStream(Base, StartPos, ContentLength); } }

    public void SaveAs(string filename) {
        byte[] work = new byte[1024 * 1024];
        int c = 0;

        using(Stream st = InputStream)
        using(FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write)) {
            while((c = st.Read(work, 0, work.Length)) > 0)
                fs.Write(work, 0, c);
        }
    }
}

後はヘルパさん達…

SubStream.cs
class SubStream : Stream {
    public SubStream(Stream s, long start, long length) {
        this.stream = s;
        this.start = start;
        this.length = length;

        this.stream.Seek(this.start, SeekOrigin.Begin);
    }

    Stream stream;
    long start;
    long length;

    public override long Length { get { return length; } }
    public override bool CanRead { get { return this.Position < length; } }
    public override bool CanWrite { get { return false; } }
    public override bool CanSeek { get { return true; } }

    public override void Flush() { }

    public override long Position {
        get { return this.stream.Position - this.start; }
        set { this.stream.Position = this.start + value; }
    }

    public override int Read(byte[] buffer, int offset, int count) {
        if(!CanRead) return 0;

        count = (int)Math.Min(count, this.Length - this.Position);
        return this.stream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin) {
        switch(origin) {
        case SeekOrigin.Begin:
            this.stream.Seek(this.start + offset, origin);
            break;

        case SeekOrigin.Current:
            this.stream.Seek(offset, origin);
            break;

        case SeekOrigin.End:
            this.Seek(this.start + this.length + offset, origin);
            break;
        }

        return this.Position;
    }

    public override void Write(byte[] buffer, int offset, int count) {
        throw new NotImplementedException();
    }

    public override void SetLength(long value) {
        throw new NotImplementedException();
    }
}


ExtClass.cs
static class ExtClass {
    static public string GetString(this Stream Me, Encoding encoding, int length) {
        byte[] work = new byte[length];
        Me.Read(work, 0, length);
        return encoding.GetString(work);
    }

    static public string GetLine(this Stream Me, Encoding encoding) {
        List<byte> work = new List<byte>();
        while(!work.IsLineEnd())
            work.Add((byte)Me.ReadByte());
        return encoding.GetString(work.ToArray()).TrimEnd('\r', '\n');
    }

    static public long SkipLine(this Stream Me) {
        for(byte b1 = (byte)Me.ReadByte(), b2 = 0; !(b2 == '\r' && b1 == '\n'); b1 = (byte)Me.ReadByte())
            b2 = b1;
        return Me.Position - 2;
    }

    static public bool IsLineEnd(this List<byte> Me) {
        if(Me.Count < 2) return false;
        return (Me[Me.Count - 2] == '\r') && (Me[Me.Count - 1] == '\n');
    }

    static public bool IsFileEnd(this Stream Me, Encoding encoding, string boundary) {
        byte[] work = encoding.GetBytes(boundary);
        foreach(byte b in work)
            if(b != Me.ReadByte()) {
                Me.Seek(-1, SeekOrigin.Current);
                return false;
            }
        return true;
    }
}



で、Startup1.csの一部を—

Startup1.csの一部
                        
    switch(context.Request.Path.Value) {
                        
                        
        case "/upload":
            FileCollection fc = FileCollection.GetFiles(context);
            for(int i = 0; i < fc.Count; i++) {
                string fileName = Path.GetFileName(fc[i].FileName);
                fileName = Regex.Replace(fileName, @"(?=\.\w+$)|(?<!\.\w+)$", DateTime.Now.ToString("yyyyMMddHHmmssfff"));
                fc[i].SaveAs(Path.Combine(@"pic", fileName));
                Thread.Sleep(1);
            }       

                        
                        
    }

こんな感じ…


実際にiPhoneと連携したい時は、デバッグ実行では無理っぽい感じなので、以下の手順で実行完了を整えてください。

  • まず、ビルドしときましょう
  • ソリューションフォルダ(TestOwin.slnがある処)からみた、packages\OwinHost.3.0.1\toolsフォルダを丸ごとデスクトップにコピーします
    PSのスクリプトファイルは気になる人は消してもいいです
  • プロジェクトフォルダ(TestOwin.csprojがある処)配下にあるbinフォルダをデスクトップに作ったtoolsフォルダにコピーします
  • toolsフォルダの中にpicフォルダを作っておいて下さい
  • あ、toolsって名前がなんだかなーって時は好きな名前に変えて貰って大丈夫

で、実行環境は整いました。
今度は実行手順です。(toolsフォルダで話を続けます)

  • 管理者でコマンドウィドウを開きます
  • cd c:\Users\■■■\Desktop\toolsでカレントディレクトリを移動します
  • OwinHost.exe -u http://192.168.3.3:5000
    で、以下のような画面が出てアクセス待ちになります image 尚、上記コマンドのIPアドレスは実行しているパソコンのアドレスを指定して、ポートは… まぁ5000を指定しとけば良いと思います
    因みに、前の方とIPアドレス体系が変わってしまっているのは、プロバイダ乗り換えたからです…(^_^;
  • アクセス待ちになったら、iPhonesafari立ち上げて、http://192.168.3.3:5000にアクセスすると…
    IISパターンと同じ画面が出るだけなので画面貼るのは省略しますが、tools\picの下に画像が転送されるのが確認出来る筈です
  • 転送が一通り終わったら、コマンド画面でEnterキーを押しましょう

一捻り

画像ファイルが転送できる事は前ケースで確認できました。
でも、前提が色々なぁ…

つまり、家にいてホームネットワークが構築されている状況が仮定されています。
ホームネットワークがない時にはどうしたら良いでしょうか?
例えば、旅行中とか、もにょもにょとか…


そういう時は、パソコンとiPhone間で直接ネットワークを構築するって技があります。
これならば、外出していても写真を吸い上げることができます。

直接ネットワークを構築する時には、パソコン側をWiFi親機にしてあげる必要があります。
SoftAPって奴ですかねぇ。
まぁ、ここら辺を確認してみて下さい。

以下ざっとした手順。(cmd.exeを管理者モードで起動しておいて下さい)

1.netsh wlan set hostednetwork mode=allow
2.netsh wlan set hostednetwork ssid=■■■■■■欄は希望のSSID)
3.netsh wlan set hostednetwork key=■■■ keyusage=persistent■■■欄は希望のパスワード)

※ここまでは一度設定したら、毎回入力は不要

4.netsh wlan start hostednetwork※転送を開始するときに入力
5.netsh wlan stop hostednetwork※転送が完了したら入力

………
なんですけど、ここまでやって衝撃の事実にぶち当たりました。
私のパソコンhostednetworkモードに対応していない…
(ぼっこいネットワークカード使いやがって、もー)


という事で、一応情報の提供までという事で。
御粗末様でした…

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0