Home > Archives > 2017年4月 Archive
2017年4月 Archive
Microsoft OneDrive for Androidでファイルをダウンロードするとデータが化けることがある、その原因とは?
Tweet | ![]() |
Microsoftがサービスを提供しているクラウドストレージサービス、OneDrive。Windows10では標準機能の一つとして存在し、最もWindowsとの親和性が高いクラウドストレージです。そして、その他のPCやモバイルデバイスでも使用可能なように、ブラウザでの操作のほか、AndroidやiOS(Macintosh, iPhone, iPadなど)、Windows Phoneでもアプリとして環境が提供されています。これにより、主要なデバイスの間でファイルのやりとりが簡単にできます。私もよく愛用しています。
説明はこれぐらいにして。
今回トラブルが発生しました。
ある一つのファイル(zipファイル、774kB)をAndroid版OneDriveアプリにてダウンロードしたところ、極端に小さいファイルが生成されました。ファイルサイズは約6kB(6.56kB)。
最初はダウンロード失敗かなと思ったのですが、5回ダウンロードして全てほぼ同じファイルサイズである6.56kBに。その後、ファイル名のリネーム、拡張子の変更、保存場所の移動、Windows10 PC上に一旦コピーして別のフォルダへの書き戻し、と行ったものの、Android版OneDriveアプリでのダウンロード結果はいずれも同じ、6.56kBのファイルがダウンロードされるだけでした。なお、この現象が発生するのはこのファイルだけで、他のzipファイルを一つダウンロードしても問題なくダウンロード出来ました。
ここで気になったのは、ほぼ同じサイズのファイルが生成されていること。こんな感じになってしまいました。
なので、一度この6.56kBのデータをBluetooth経由でWindows PCにコピーして中身を確認してみました。
すると、中身はなんとHTMLデータ。
タイトルタグは「Microsoft OneDrive - Access files anywhere. Create docs with free Office Online.」
そしてこのHTMLファイルはリダイレクトが設定されていたのでURLを確認すると、「26avres%3DScanError%26averror%3DLIMIT_REACHED%26page%3Dviruswarning&」という文字列が含まれていました。
ひょっとして・・・?と思い、このファイルの拡張子をHTMLに変更した上でブラウザで開いてみると、下記のような画面が表示されました。
つまり、OneDriveがブラウザからのダウンロード時に行っているウイルススキャンが実行完了できず、そのために警告を出している画面でした。(ウイルススキャンはどのタイミングで行っているかは正確には不明です)
これが、Android版OneDriveアプリでのダウンロード時のデータ化けの正体でした。つまり、Android版OneDriveアプリでのダウンロードはブラウザと同じアクセス方法で行っていると推測されるため、それゆえウイルススキャン画面に移動、ただしアプリではそれを感知できず、表示された画面=ダウンロードすべきファイルと判断してダウンロードしたのだと考えられます。
こんなこともあるんだなぁと感じた日でした。
ちなみに、このファイルの中身はtar.gzファイルなのですが、その解凍に極めて時間がかかるものだったので、途中でウイルススキャンがギブアップ、いや、タイムアウトしたのではないかと。
Twitterのタイムライン(TL)をnode+MongoDBで保存、Ruby+Sinatraで表示できるようにしてみた。
Tweet | ![]() |
Twitter、私も利用しているのですが、フォロー数が多くなると問題となるタイムラインの流速問題。そうでなくても、公式クライアント含む各種Twitterではタイムラインの保存に限度があるので、過去の物を検索しようとしても不可能か、かなり困難です。
ならばTLを保存すれば良いのではないか?ということで、いろいろ調べた結果、「Twitter APIで取得できるデータはJSON形式、なのでMongoDBと親和性高い」ということで、この方向で進めることに。
参考にしたサイトが多数にわたるため、本記事の最後に記載します。参考にさせて頂いたことに感謝します。
1. 前準備
まずはRuby、node、MongoDBをインストール。私の環境はubuntu16.04 LTSですが、他のLinuxディストリビューションでも同様の環境は準備可能だと思います。ただ、できればOS/MongoDBは64bitを推奨。(データベースのサイズ制限、メモリ制限ほか様々な制限のため)
また、各ソフトウェアの細かい設定はしていない(標準設定のまま)なので、必要に応じ情報を収集して対応して下さい。
sudo apt install ruby
sudo apt install nodejs-legacy
sudo apt install npm
MongoDBについては、ubuntu16.04 LTSで標準のaptリポジトリでインストールされるパッケージが古すぎる(2.6.x系)ので、MongoDBの公式のインストール手順に従って3.x系をインストールして下さい。(2.6.x系だとソート時にソート用バッファメモリ不足が発生し、かつ回避が困難です)
Install MongoDB Community Edition on Ubuntu -- MongoDB Manual 3.4
https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
一応、MongoDBが64bitでインストールされているか確認。
user@hoge:~$ mongo
> use admin
switched to db admin
> db.runCommand("buildInfo")
(中略)
"bits" : 64,
(後略)
問題無さそう。
ライブラリも必要。(途中で必要になる)
sudo apt install ruby-dev
次はnode用パッケージのインストール。
次はRuby-gemのインストール
2. MongoDBのアクセス制限の設定
MongoDB 3.6はパフォーマンスやセキュリティの関係で色々ワーニングが出ます。公式マニュアルにも記載がありますが、他にも日本語での説明ページがあったのでリンクしておきます。
MongoDB 3系を CentOS7 にインストール - Qiita
http://qiita.com/SOJO/items/dc5bf9b4375eab14991b
これ以外にも公式マニュアル他を参照して設定して下さい。まあデフォルトのままでも動きますけどね・・・。
3. TwitterのAPI KEYを取得する。
これはあちこちに解説記事があるので省略したい。必要な権限はツイートの読み取り権限のみです。書き込みはしないので今回は不要です。
consumer_key, consumer_secret, access_token_key, access_token_secretの4つのキーを後で使います。
一応、API申請ページにリンクしておきます。
4. Twitter TL取得用のスクリプトの作成。(前準備含む)
タイムライン取得にはMongoDBと親和性のあるNode.jsを利用します。まずはスクリプトの保存フォルダの作成と必要なライブラリの入手。
$ mkdir tw2db
$ cd tw2db
$ npm install mongoose
$ npm install twitter
次に、スクリプトの作成。これは下記サイトの内容そのままで動いたので丸パクリです(汗)
ただし、MongoDBのユーザー認証を有効にしているので、そこだけ改変しています。
Twitter の TL を全部 MongoDB にぶち込んでニヤニヤする - 凹みTips
var twitter = require('twitter')
, mongoose = require('mongoose')
, Schema = mongoose.Schema
;
// typeof で得た文字列を型に変換
var typeMap = {
number : Number,
string : String,
boolean : Boolean,
object : Object,
function : Function
};
// オブジェクト/配列を受け取って Mongoose 用 Schema に変換
function makeSchema(data) {
var schema = {};
for (var x in data) {
var type = typeof data[x];
if (data[x] === null) {
schema[x] = Object;
} else if (type === 'object') {
schema[x] = makeSchema(data[x]) ;
} else {
schema[x] = typeMap[type];
}
}
return schema;
}
// MongoDB へ接続
mongoose.connect('mongodb://dbuser:dbpasswd@localhost/Twitter');
// mongoose のスキーマ
var PostSchema, Post, isSchemaDefined = false;
// Twitter へ接続
new twitter({
consumer_key : 'XXXXXXXXXXXXXXXXXXXXXXXX',
consumer_secret : 'XXXXXXXXXXXXXXXXXXXXXXXX',
access_token_key : 'XXXXXXXXXXXXXXXXXXXXXXXX',
access_token_secret : 'XXXXXXXXXXXXXXXXXXXXXXXX'
}).stream('user', function(stream) {
stream.on('data', function(data) {
// Friends リストのデータはすっ飛ばす
if ( !('id' in data) ) {
return;
} else {
console.log(data.user.screen_name, data.text);
}
// 最初のデータで Schema を作成
if (!isSchemaDefined) {
PostSchema = new Schema( makeSchema(data, '') )
Post = mongoose.model('Post', PostSchema)
isSchemaDefined = true;
}
// Post Schema から保存用のデータを生成して保存
var post = new Post(data);
post.save( function(err) {
if (err) console.error(err);
});
});
});
// 例外処理
process.on('uncaughtException', function (err) {
console.log('uncaughtException => ' + err);
});
もちろんconsumer_key, consumer_secret, access_token_key, access_token_secretの部分は2.で取得した情報と書き換えて下さいね。
あと、ユーザー認証をする場合とユーザー認証をしない場合で、下記の行の修正が若干異なります。
*ユーザー認証する場合
mongoose.connect('mongodb://dbuser:dbpasswd@localhost/Twitter');
上記のdbuser、dbpasswdを置き換えて下さい。
*ユーザー認証しない場合
mongoose.connect('mongodb://localhost/Twitter');
あとは、動作テスト。
node tw2db.js
これで、自分のタイムラインに流れているツイートと同じものが流れてくるはずです。
5. Node.jsの自動起動と、自動Kill(cronで)
まずはNode.jsの起動を自動化します。これは下記ページを参考にして下さい。私はinit.dでサービス起動する形にしました。
initd-foreverでNode.jsアプリをデーモン化する | Developers.IO
http://dev.classmethod.jp/server-side/daemonize-nodejs-by-initd-forever/
Node.jsが重すぎるのか、一日以上経過すると頻繁にフリーズしているので、自動killするためのスクリプトを作ります。 cronで実行するので、シェルスクリプトに。ファイル名は例えば「autokill.sh」とかで。もちろんchmodで実行可能にするのを忘れずに。
#!/bin/sh
pid=`ps -elf | grep "tw2db.js" | grep -v grep | awk '{print $4}'`
if [ "${pid}" != "" ]; then
kill ${pid}
echo "kill ${pid}"
fi
sleep 2
nohup /usr/bin/node /path/to/tw2db/tw2db.js > /dev/null &
あとはcronに登録しておいて下さい。だいたい1日1回ぐらいでも良いと思います。私は1時間に一回(毎時40分頃)にしていますが・・・。
6. ビューア部分の作成(Ruby+Sinatra)
ある意味ここからが本番。
ビューアに必要なライブラリはこちら。
$ gem install mongo
$ gem install twitter
$ gem install sinatra
$ gem install sinatra-i18n
$ gem install rack-contrib
他にもあったかも・・・
そして、私が作成したスクリプトがこちら。なお、以下5つのファイル/フォルダを作成しています。
~/tw2db/viewer.rb
~/tw2db/views/tweets.erb
~/tw2db/config/application.rb
~/tw2db/config/locales/en.yml
~/tw2db/config/locales/ja.yml
~/tw2db/viewer.rb
require 'rubygems'
require 'mongo'
require 'sinatra'
require 'sinatra/i18n'
require 'rack/contrib'
require 'date'
require 'rack'
use Rack::Deflater
Sinatra.register Sinatra::I18n
#use Rack::Locale
#require './config'
CONNECTION_STRING = "mongodb://dbuser:dbpasswd@localhost/Twitter"
COLLECTION_NAME = "posts"
TAGS = ["mongodb","ruby"]
set :environment, :production
# The Unix epoch is the time 00:00:00 UTC on January 1, 1970
UNIX_EPOCH_TIME = Time.at(0)
# Strict version of +Time.parse+, returns +nil+ when parsing is failed.
def strict_parsetime(string)
# +Time.parse+ returns localtime "1970/01/01 00:00:00" when parsing is failed.
# So, ugly, I check whether returned value is UNIX epoch.
time = Time.parse(string, UNIX_EPOCH_TIME) rescue nil
if UNIX_EPOCH_TIME == time then
# Previous +Time.parse+ possibly failed.
time = nil unless (ParseDate.parsedate(string)[0] rescue nil)
end
time
end
def tweet_id2time(id)
case id
when Integer
Time.at(((id >> 22) + 1288834974657) / 1000.0)
else
nil
end
end
def time2tweet_id(time)
(time.to_f * 1000 - 1288834974657).to_i << 22
end
configure do
db = Mongo::Client.new(CONNECTION_STRING)
TWEETS = db[COLLECTION_NAME]
I18n.default_locale = :ja
I18n.locale = :ja
end
get '/' do
selector = {}
@search_text=""
@from_date=Time.at(1288834974657/1000)
@to_date=Time.now
@tweets = TWEETS.find({:id => {'$gte'=> (time2tweet_id(@from_date)),'$lte'=>(time2tweet_id(@to_date))}}).sort({"id" => -1}).limit(10000)
@twcount=@tweets.count()
erb :tweets
end
post '/' do
@from_date=strict_parsetime(params[:from_date])
@to_date=strict_parsetime(params[:to_date])
if @from_date==nil
@from_date=Time.at(1288834974657/1000)
end
if @to_date==nil
@to_date=Time.now
end
@search_text=params[:search_text]
@tweets = TWEETS.find({'$and':[{:text => Regexp.new(@search_text)},{:id => {'$gte'=> (time2tweet_id(@from_date)),'$lte'=>(time2tweet_id(@to_date))}}]}).sort({"id" => -1})
@twcount=@tweets.count()
erb :tweets
end
今回のスクリプトは外部接続を受け付ける設定「set :environment, :production がそれ」にしています。ここは必要に応じ改変して下さい。
ユーザー認証をする場合とユーザー認証をしない場合で、下記の行の修正が若干異なります。
*ユーザー認証する場合
CONNECTION_STRING = "mongodb://dbuser:dbpasswd@localhost/Twitter"
上記のdbuser、dbpasswdを置き換えて下さい。
*ユーザー認証しない場合
CONNECTION_STRING = "mongodb://localhost/Twitter"
~/tw2db/views/tweets.erb
<!DOCTYPE>
<html>
<head>
<style>
body{
width:1000px;
margin: 50px auto;
}
h2{
margin-top:2em;
}
pre {
/* Mozilla */
white-space: -moz-pre-wrap;
/* Opera 4-6 */
white-space: -pre-wrap;
/* Opera 7 */
white-space: -o-pre-wrap;
/* CSS3 */
white-space: pre-wrap;
/* IE 5.5+ */
word-wrap: break-word;
}
</style>
<title>Tweet Archive</title>
</head>
<body>
<h1>Tweet Archive</h1>
search by Time/Date <form action="/" method="post">
<br>
search string(Regexp)<input type="text" name="search_text" placeholder"文字列を 入れると検索します" value="<%= @search_text %>"><br>
since<input type="text" name="from_date" placeholder="yyyy/mm/dd hh:mm:ss" value="<%= @from_date %>"><br>
until<input type="text" name="to_date" placeholder="yyyy/mm/dd hh:mm:ss" value="<%=
@to_date %>"><br>
<input type="submit">
</form>
<% TAGS.each do |tag| %>
<a href="/?tag=<%= tag %>"><%= tag %></a>
<% end %>
<br>
search count:<%= @twcount %> <br>
<% @tweets.each do |tweet| %>
<hr>
<pre><h2><font size=6><%= tweet['text'] %></font></h2></pre>
<p>
<a href="http://twitter.com/<%= tweet['user']['screen_name'] %>">
<%= tweet['user']['name'] %>
</a>
on <a href="https://twitter.com/<%= tweet['user']['screen_name'] %>/statuses/<%= tweet['id_str'] %>" target="_blank"><%= I18n.l Time.parse( tweet['created_at']).to_time , format: :long %></a><%= Time.at(((tweet['id_str'].to_i >> 22 )+1288834974657)/1000.0).strftime("%Y-%m-%d %H:%M:%S.%L %Z") %>
</p>
<img src="<%= tweet['user']['profile_image_url'] %>" width="48" />
<% end %>
</body>
</html>
~/tw2db/config/application.rb
config.time_zone = 'Tokyo'
config.i18n.default_locale = :ja
~/tw2db/config/locales/en.yml、 ~/tw2db/config/locales/ja.yml (どちらも全く同じ内容です)
ja:
time:
formats:
default: ! '%Y/%m/%d'
long: ! '%Y年%m月%d日 %H時%M分%S秒 %z'
short: ! '%Y年%m月%d日 %H:%M'
en:
time:
formats:
default: ! '%Y/%m/%d'
long: ! '%Y年%m月%d日 %H時%M分%S秒 %z'
short: ! '%Y年%m月%d日 %H:%M'
あとはスクリプトを実行して、ブラウザからアクセスします。
ruby viewer.rb
立ち上げたサーバーのポート4567にアクセスすればOKです。
例:http://192.168.0.1:4567/
検索部分は正規表現ですが、あまり高機能じゃないので過信しないように。
X.参考にしたサイト群
今回は以下のサイトのスクリプトや情報を元に活用しました。本当に感謝です。
* MongoDBのインストール
MongoDBの薄い本(The Little MongoDB Book) - cuspy diary
http://www.cuspy.org/diary/2012-04-17/* Twitter TLをMongoDBに保存
Twitter の TL を全部 MongoDB にぶち込んでニヤニヤする - 凹みTips
http://tips.hecomi.com/entry/20120908/1347094725
* forever と initrd-foreverで死活監視
Node.js 自動再起動モジュール - Qiita
http://qiita.com/disc99/items/57490f5eef3e2eb685ba
node.js node.jsスクリプトをforeverでデーモン化する -でじうぃき
initd-foreverでNode.jsアプリをデーモン化する | Developers.IO
http://dev.classmethod.jp/server-side/daemonize-nodejs-by-initd-forever/
* node.jsの自動Kill(頻繁に固まるので)
node.jsの自動再起動 | 爆裂健.com
http://bakuretuken.com/node-js%E3%81%AE%E8%87%AA%E5%8B%95%E5%86%8D%E8%B5%B7%E5%8B%95/
* Ruby+Sinatraでログを表示する
Ruby MongoDB イン・アクション のTweetArchiverを作成する - 1.21 jigowatts
http://sh-yoshida.hatenablog.com/entry/2016/08/27/025808
* Ruby+Sinatraでpostデータ処理(検索用)
Sinatraでフォームからデータを受け取る方法
http://ruby.weva.jp/sinatra/2013/09/10/params.html
* Rubyでの日付文字列の処理の厳格化
Ruby の Time.parse で文字列を Time に変換するときのエラーチェック
http://www.metareal.org/2007/06/21/error-checking-in-ruby-time-parsing/
*Ruby+Sinatraの日付のロケール処理
RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い - Qiita
http://qiita.com/jnchito/items/cae89ee43c30f5d6fa2c
Hideki SAKAMOTO の雑記 (2010-09-26)
http://www.on-sky.net/~hs/index.cgi?date=20100926
Sinatra Recipes - Development - I18n
http://recipes.sinatrasapporo.org/p/development/i18n?
- 2進数時計
-
※クリックで読みやすくなります。
※この時計の時刻は、閲覧しているパソコンのものであり、必ずしも正確な時間とは限りません - Search
-
カスタム検索
- Feeds
- Google Adsense
- Tag Cloud
-
- 0x000000fe
- 0x7e
- 109枚
- 12枚
- 19
- 2010
- 2011
- 2011年
- 2GB
- 32bit
- 3D
- 44
- 53人
- 64bit
- 848枚
- 90
- ActionScript
- Adobe
- AHCI
- AKB
- AKB48
- AKB48商法
- AKB商法
- Amazonアソシエイト
- AMD
- Android
- Android 4.0
- ANKER
- Aprilfool
- ASUS
- au
- B-CAS
- BD
- Blu-ray
- BSoD
- BSOD
- BUFFALO
- C
- DaemonTools
- DATE
- DATETIME
- dbx
- dd
- docomo
- DoCoMo
- Driver
- dump
- DVD
- DVD-Video
- epgrec
- Excel
- Express
- Filter
- FireFox
- Flash
- Friio
- fsck
- G15
- G700
- Gavotte Ramdisk
- Google Adsense
- google chrome
- grub
- HDD
- HDDケース
- IDE
- IE8
- Internet Explorer 8
- InternetExplorer
- IPoE
- IPsec
- IPv6
- IRC
- ISO
- ITPROTECH
- JASRAC
- KASRAC
- KDDI
- Keyboard
- L2TP
- linux
- Linux
- logicool
- Logicool
- LVM
- m555b
- m557b
- Media Player
- mhm
- Microsoft
- Microsoft Security Essentials
- MITSUBISHI
- mod_rewrite
- mopera
- Movable type 3.2
- Movable Type 4
- mt.Vicuna
- MT3.2
- MT4
- MySQL
- NGN
- NTFS
- NTT東日本
- NTT西日本
- Office
- OutlookExpress
- OutlookExpress、Outlook
- PCワンズ
- Player
- PowerDVD
- PPPoE
- PPTP
- PSP
- PT1
- PT2
- PT3
- qi
- Ramディスク
- RDT233WX-3D
- restore
- RewriteEngine
- RewriteRule
- RightWands
- RMA
- s.ytimg.com
- Saba
- Samsung
- Scroll
- SDHC
- SDカード
- SE-U55SX
- Seagate
- Seatools
- service Pack 1
- service Pack 2
- SideWinder
- SiI3124
- SMART
- Smooth
- SoftBank
- SPモード
- squid
- SSD
- stderr
- stdin
- stdout
- Sun VirtualBox
- TIME
- TIMESTAMP
- Transcend
- unattend
- unix
- UNIX_TIMESTAMP
- USB
- USB3.0
- USB3.0ハブ
- USBハブ
- USBメモリ
- USB扇風機
- Ustream
- VANTEC
- VHD
- Virtual CloneDrive
- Virtual PC
- VirtualBOX
- VirtualBox
- Vista
- VMWare
- VORTEX
- VPN
- vsftpd
- WesturnDigital
- Wi-FI
- Windows
- Windows 7
- Windows 8
- Windows 8.1
- Windows 98
- Windows Server 2012
- Windows Vista
- Windows XP
- WinDVD
- Winny
- WZR-HP-G450H
- X4
- YouTube
- Zenfone2
- あずまんが大王
- おり娘
- こなた
- ぼったくり
- らき☆すた
- アキバ48
- アナログ入力
- イオシス
- インストール
- ウイルス
- ウイルスバスター
- エイプリルフール
- エコポイント
- エゴポイント
- エラー
- オリコ
- カスラック
- カッツン
- カテゴリ
- カルト宗教
- カード
- クレジットカード
- ゲーム
- サイズ
- シャットダウン
- スクロール
- スマートフォン
- スムース
- スムーズ
- セットアップ
- ゼルダの伝説
- ソフトウェア
- ソフトバンク
- ディスプレイ
- デーモンツールズ
- トラブル
- トランセンド
- トレンドマイクロ
- ドコモ
- ネット流行語大賞
- ハードウェア
- パーティション
- ファイアウォール
- フラッシュメモリ
- フリーオ
- フリーズ
- フレッツ
- ブルースクリーン
- ブルーバック
- ブルーレイ
- ブログ
- プレーヤー
- プログラム
- マウス
- マウント
- マクロ
- メモリ
- メール
- メールフォルダ
- メールボックス
- リダイレクト
- レコーダー
- レビュー
- ロジクール
- ワンズちゃんす
- 三菱
- 不幸袋
- 不良
- 不良セクタ
- 今年の漢字
- 仮想ハードディスク
- 仮想環境
- 伊勢湾岸自動車道
- 休止状態
- 伝説
- 修復
- 修理
- 充電器
- 光デジタル出力
- 公明党
- 再構築
- 再起動
- 初期不良
- 利権団体
- 創価学会
- 受信できない
- 右クリックメニュー
- 四月馬鹿
- 地デジ
- 地上デジタル放送
- 壊れた
- 大川隆法
- 天下り
- 失敗
- 奇跡
- 容量
- 幸福の科学
- 幸福実現党
- 強制終了
- 復活
- 感想
- 拡張ボード
- 携帯
- 攻略
- 故障
- 新名神
- 日本UNICEF協会
- 日本ユニセフ協会
- 日本音楽著作権管理団体
- 東名阪
- 東方
- 極悪
- 機種変更
- 正規表現
- 死ね
- 池田大作
- 池田犬作
- 流行語大賞
- 消えた
- 消える
- 消えろ
- 消滅しろ
- 涼宮ハルヒちゃん
- 無人セットアップ
- 無人応答ファイル
- 犬川隆法
- 異音
- 真似できない
- 神々のトライフォース2
- 秋元康
- 空っぽ
- 置くだけ充電
- 衆議院議員選挙
- 衆院選
- 詐欺
- 警告
- 販売手数料
- 超利権団体
- 送信できない
- 送受信できない
- 速度
- 遅い
- 金子勇
- 頭金
- 鯖
- 麻雀