分享

融云如何实现文件发送(高级进阶)

 WindySky 2016-04-25

想在聊天中发 小视频gif 动图发红包? 发 自定义表情? 没有问题!在融云统统都可以实现! 以上不管是 小视频 还是 gif 还是 红包 或者是 自定义表情 归根结底都是数据的传输 文件的传输. 后缀无非是 .png .gif .mp4 .amr 等 所以我们只要掌握了文件发送上面的所有需求都能根据文件消息衍生实现。 那我们就来赶紧切入正题看看文件消息的实现和文件消息的收发吧 Let’s Go!


先来看看效果:

pic


接下来这边会把是实现原理 和 实现代码给大家分享

实现原理:

消息需要自定义消息类型 使用融云的消息收发通道 UI展示 需要使用到 融云消息模板 可以自定义UI展示样式 利用 后台存储 或者 云存储(此处示例七牛) 上传的文件 再给予这个文件一个 可下载的公网路径(例:http://xxx & https://xxx) 发消息其实就是把本地文件上传(你需要有个 upLoadFile 类似的方法), 以及上传进度提示 收到消息 用户点击消息 此时就是获取消息体中包含的 retomeUrl 然后去下载 (你需要有个 downLoadFile 类似的方法) 下载下来以后对文件然后根据自身 App 逻辑需求自行处理

实现代码:

FileMessage 此类是 文件消息 实体类 继承自融云 MessageContent FileMessageProvider 此类是 文件消息模板 负责消息在会话界面 UI 的展示 SendFileProvider 此类是发送文件消息的入口
以上是三个最主要的逻辑代码类, 先大概说下用途. 还有一些细节代码.后续会跟上代码再做详细分享

FileMessage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
<code class="hljs java">package io.rong.message;
import android.annotation.SuppressLint;
import android.net.Uri;
import android.os.Parcel;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import io.rong.common.ParcelUtils;
import io.rong.imlib.MessageTag;
import io.rong.imlib.model.MessageContent;
import io.rong.imlib.model.UserInfo;
/**
 * Created by Bob on 15/12/24.
 */
@SuppressLint("ParcelCreator")
@MessageTag(value = "RC:FileMsg", flag = MessageTag.ISCOUNTED | MessageTag.ISPERSISTED, messageHandler = FileMessageHandler.class)
public class FileMessage extends MessageContent {
    private Uri mThumUri;
    private Uri mLocalUri;
    private Uri mRemoteUri;
    private boolean mUpLoadExp = false;
    private String mBase64;
    boolean mIsFull;
    protected String extra;
    /**
     * 获取消息附加信息
     *
     * @return 附加信息
     */
    public String getExtra() {
        return extra;
    }
    /**
     * 设置消息附加信息
     *
     * @param extra 附加信息
     */
    public void setExtra(String extra) {
        this.extra = extra;
    }
    public FileMessage(byte[] data) {
        String jsonStr = new String(data);
        try {
            JSONObject jsonObj = new JSONObject(jsonStr);
            if (jsonObj.has("fileUri")) {
                String uri = jsonObj.optString("fileUri");
                if(!TextUtils.isEmpty(uri))
                    setRemoteUri(Uri.parse(uri));
                if (getRemoteUri() != null && getRemoteUri().getScheme() != null && getRemoteUri().getScheme().equals("file")) {
                    setLocalUri(getRemoteUri());
                }
            }
            if (jsonObj.has("content")) {
                setBase64(jsonObj.optString("content"));
            }
            if (jsonObj.has("extra")) {
                setExtra(jsonObj.optString("extra"));
            }
            if (jsonObj.has("exp")) {
                setUpLoadExp(true);
            }
            if (jsonObj.has("isFull")) {
                setIsFull(jsonObj.optBoolean("isFull"));
            }
            if (jsonObj.has("user")) {
                setUserInfo(parseJsonToUserInfo(jsonObj.getJSONObject("user")));
            }
        } catch (JSONException e) {
            Log.e("JSONException", e.getMessage());
        }
    }
    public FileMessage() {
    }
    private FileMessage(Uri thumbUri, Uri localUri) {
        mThumUri = thumbUri;
        mLocalUri = localUri;
    }
    private FileMessage(Uri thumbUri, Uri localUri, boolean original) {
        mThumUri = thumbUri;
        mLocalUri = localUri;
        mIsFull = original;
    }
    /**
     * 生成FileMessage对象。
     *
        */
    public static FileMessage obtain(Uri thumUri, Uri localUri) {
        return new FileMessage(thumUri, localUri);
    }
    /**
     * 生成FileMessage对象。
     *
     */
    public static FileMessage obtain(Uri thumUri, Uri localUri, boolean isFull) {
        return new FileMessage(thumUri, localUri, isFull);
    }
    /**
     * 生成FileMessage对象。
     *
     * @return ImageMessage对象实例。
     */
    public static FileMessage obtain() {
        return new FileMessage();
    }
    /**
     * 获取缩略图Uri。
     *
     * @return 缩略图Uri(收消息情况下此为内部Uri,需要通过ResourceManager.getInstance().getFile(new Resource(Uri))方式才能获取到真实地址)。
     */
    public Uri getThumUri() {
        return mThumUri;
    }
    /**
        *
     * @return true / false
     */
    public boolean isFull() {
        return mIsFull;
    }
    /**
     * 设置发送原图标志位。
     *
         */
    public void setIsFull(boolean isFull) {
        this.mIsFull = isFull;
    }
    /**
     * 设置缩略图Uri。
     *
     * @param thumUri 缩略图地址
     */
    public void setThumUri(Uri thumUri) {
        this.mThumUri = thumUri;
    }
    /**
     * 获取本地图片地址(file:///)。
     *
     * @return 本地图片地址(file:///)。
     */
    public Uri getLocalUri() {
        return mLocalUri;
    }
    /**
     * 设置本地图片地址(file:///)。
     *
     * @param localUri 本地图片地址(file:///).
     */
    public void setLocalUri(Uri localUri) {
        this.mLocalUri = localUri;
    }
    /**
     * 获取网络图片地址(http://)。
     *
     * @return 网络图片地址(http://)。
     */
    public Uri getRemoteUri() {
        return mRemoteUri;
    }
    /**
     * 设置网络图片地址(http://)。
     *
     * @param remoteUri 网络图片地址(http://)。
     */
    public void setRemoteUri(Uri remoteUri) {
        this.mRemoteUri = remoteUri;
    }
    /**
     * 设置需要传递的Base64数据
     *
     * @param base64 base64数据。
     */
    public void setBase64(String base64) {
        mBase64 = base64;
    }
    /**
     * 获取需要传递的Base64数据。
     *
     * @return base64数据。
     */
    public String getBase64() {
        return mBase64;
    }
    /**
     * 是否上传失败。
     *
     * @return 是否上传失败。
     */
    public boolean isUpLoadExp() {
        return mUpLoadExp;
    }
    /**
     * 设置是否上传失败。
     *
     * @param upLoadExp 上传是否失败。
     */
    public void setUpLoadExp(boolean upLoadExp) {
        this.mUpLoadExp = upLoadExp;
    }
    @Override
    public byte[] encode() {
        JSONObject jsonObj = new JSONObject();
        try {
            if (!TextUtils.isEmpty(mBase64)) {
                jsonObj.put("content", mBase64);
            } else {
                Log.d("ImageMessage", "base64 is null");
            }
            if (mRemoteUri != null) {
                jsonObj.put("fileUri", mRemoteUri.toString());
            } else if (getLocalUri() != null) {
                jsonObj.put("fileUri", getLocalUri().toString());
            }
            if (mUpLoadExp) {
                jsonObj.put("exp", true);
            }
            jsonObj.put("isFull", mIsFull);
            if (!TextUtils.isEmpty(getExtra()))
                jsonObj.put("extra", getExtra());
            if (getJSONUserInfo() != null)
                jsonObj.putOpt("user", getJSONUserInfo());
        } catch (JSONException e) {
            Log.e("JSONException", e.getMessage());
        }
        mBase64 = null;
        return jsonObj.toString().getBytes();
    }
    /**
     * 描述了包含在 Parcelable 对象排列信息中的特殊对象的类型。
     *
     * @return 一个标志位,表明Parcelable对象特殊对象类型集合的排列。
     */
    @Override
    public int describeContents() {
        return 0;
    }
    /**
     * 构造函数。
     *
     * @param in 初始化传入的 Parcel。
     */
    public FileMessage(Parcel in) {
        setExtra(ParcelUtils.readFromParcel(in));
        mLocalUri = ParcelUtils.readFromParcel(in, Uri.class);
        mRemoteUri = ParcelUtils.readFromParcel(in, Uri.class);
        mThumUri = ParcelUtils.readFromParcel(in, Uri.class);
        setUserInfo(ParcelUtils.readFromParcel(in, UserInfo.class));
        mIsFull = ParcelUtils.readIntFromParcel(in) == 1;
    }
    /**
     * 将类的数据写入外部提供的 Parcel 中。
     *
     * @param dest  对象被写入的 Parcel。
     * @param flags 对象如何被写入的附加标志,可能是 0 或 PARCELABLE_WRITE_RETURN_VALUE。
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        ParcelUtils.writeToParcel(dest, getExtra());
        ParcelUtils.writeToParcel(dest, mLocalUri);
        ParcelUtils.writeToParcel(dest, mRemoteUri);
        ParcelUtils.writeToParcel(dest, mThumUri);
        ParcelUtils.writeToParcel(dest, getUserInfo());
        ParcelUtils.writeToParcel(dest, mIsFull ? 1 : 0);
    }
    /**
     * 读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。
     */
    public static final Creator<filemessage> CREATOR = new Creator<filemessage>() {
        @Override
        public FileMessage createFromParcel(Parcel source) {
            return new FileMessage(source);
        }
        @Override
        public FileMessage[] newArray(int size) {
            return new FileMessage[size];
        }
    };
}</filemessage></filemessage></code>

自定义消息类里面主要做了消息的持久序列化 和 消息包含的内容和成员, 例如上传文件我肯定需要知道文件的本地路径,例如发送红包消息我们需要知道红包的金额


FileMessageProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<code class="language-java hljs ">package io.rong.app.message.provider;
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.sea_monster.resource.Resource;
import io.rong.app.R;
import io.rong.imkit.RongContext;
import io.rong.imkit.model.ProviderTag;
import io.rong.imkit.model.UIMessage;
import io.rong.imkit.widget.AsyncImageView;
import io.rong.imkit.widget.provider.IContainerItemProvider;
import io.rong.imlib.model.Message;
import io.rong.message.FileMessage;
/**
 * Created by Bob on 15/12/24.
 */
@ProviderTag(messageContent = FileMessage.class, showPortrait = true, showProgress = true, centerInHorizontal = false)
public class FileMessageProvider extends IContainerItemProvider.MessageProvider<filemessage> {
    /**
     * 初始化View
     */
    @Override
    public View newView(Context context, ViewGroup group) {
        View view = LayoutInflater.from(context).inflate(R.layout.de_item_file_message, group,false);
        ViewHolder holder = new ViewHolder();
        holder.message = (TextView) view.findViewById(R.id.rc_msg);
        holder.img = (AsyncImageView) view.findViewById(R.id.rc_img);
        view.setTag(holder);
        return view;
    }
    @Override
    public void bindView(View v, int position, FileMessage content, UIMessage message) {
        final ViewHolder holder = (ViewHolder) v.getTag();
        if (message.getMessageDirection() == Message.MessageDirection.SEND) {
            v.setBackgroundResource(io.rong.imkit.R.drawable.rc_ic_bubble_no_right);
        } else {
            v.setBackgroundResource(io.rong.imkit.R.drawable.rc_ic_bubble_no_left);
        }
        holder.img.setResource(content.getThumUri() == null null : new Resource(content.getThumUri()));
        int progress = message.getProgress();
        Message.SentStatus status = message.getSentStatus();
        if (status.equals(Message.SentStatus.SENDING) && progress < 100) {
            if (progress == 0)
                holder.message.setText(RongContext.getInstance().getResources().getString(io.rong.imkit.R.string.rc_waiting));
            else
                holder.message.setText(progress + "%");
            holder.message.setVisibility(View.VISIBLE);
        } else {
            holder.message.setVisibility(View.GONE);
        }
    }
    @Override
    public Spannable getContentSummary(FileMessage data) {
        return new SpannableString(RongContext.getInstance()
                .getResources()
                .getString(R.string.de_plugins_file));
    }
    @Override
    public void onItemClick(View view, int position, FileMessage content, UIMessage message) {
    }
    @Override
    public void onItemLongClick(View view, int position, FileMessage content, UIMessage message) {
    }
    class ViewHolder {
        AsyncImageView img;
        TextView message;
    }
}</filemessage></code>

FileMessageProvider 负责控制 UI 展示样式的类 此处就用一个文件样式做展示,如果是发红包可能资源图片就会换成红包样式的图片,还有上传进度的 UI 展示也在本类


SendFileProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<code class="hljs java">package io.rong.app.message.provider;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import java.io.File;
import io.rong.imkit.RongContext;
import io.rong.imkit.RongIM;
import io.rong.imkit.widget.provider.InputProvider;
import io.rong.imlib.RongIMClient;
import io.rong.imlib.model.Conversation;
import io.rong.imlib.model.Message;
import io.rong.message.FileMessage;
/**
 * Created by AMing on 15/12/24.
 * Company RongCloud
 */
public class SendFileProvider extends InputProvider.ExtendProvider {
    private static final String TAG = SendFileProvider.class.getSimpleName();
    private Context context;
    /**
     * 实例化适配器。
     *
     * @param context 融云IM上下文。(通过 RongContext.getInstance() 可以获取)
     */
    public SendFileProvider(RongContext context) {
        super(context);
        this.context = context;
    }
    @Override
    public Drawable obtainPluginDrawable(Context context) {
        return context.getResources().getDrawable(io.rong.imkit.R.drawable.rc_ic_picture);
    }
    @Override
    public CharSequence obtainPluginTitle(Context context) {
        return "文件";
    }
    @Override
    public void onPluginClick(View view) {
//        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
//        intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
//        startActivityForResult(intent, 1);
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.setType("file/*");
        startActivityForResult(i, 1);
    }
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data != null) {
            if (data.getData().getScheme().equals("file")) {
                String s = data.getData().getPath();
                Uri uri = data.getData();
                File file = new File(s);
                if (file.exists()) {
                    Log.e("file", "f是文件且存在");
                   Conversation conversation = getCurrentConversation();
                   sendFile(conversation.getConversationType(),conversation.getTargetId(),file,uri);
                } else {
                    Toast.makeText(context,"文件不存在",Toast.LENGTH_SHORT);
                }
            }
        }
    }
    private void sendFile(Conversation.ConversationType conversationType, String id, File file,Uri uri) {
        if (RongIM.getInstance()!= null && RongIM.getInstance().getRongIMClient() != null) {
            //TODO 文件消息
            Log.e("tag","");
            Uri themUri = Uri.parse("file:///sdcard/bob/bob.zip" );
//            Uri localUri = Uri.parse("file:///sdcard/bob/bob.zip");
            FileMessage fileMessage = FileMessage.obtain(uri,uri);
//            ImageMessage fileMessage = ImageMessage.obtain(themUri,localUri);
            RongIM.getInstance().getRongIMClient().sendImageMessage(Conversation.ConversationType.PRIVATE, id, fileMessage, null, null, new RongIMClient.SendImageMessageCallback() {
                @Override
                public void onAttached(Message message) {
                    Log.e(TAG, "-------------onAttached--------");
                }
                @Override
                public void onError(Message message, RongIMClient.ErrorCode code) {
                    Log.e(TAG, "----------------onError-----" + code);
                }
                @Override
                public void onSuccess(Message message) {
                    Log.e(TAG, "------------------onSuccess---");
                }
                @Override
                public void onProgress(Message message, int progress) {
                    Log.e(TAG, "-----------------onProgress----" + progress);
                }
            });
        }
    }
}
</code>

本类是发送文件的入口类,需要提醒的是. 此处代码中是直接开启的 Android 系统的文件管理器,在某些基于 Android 自定义的机型里面打开没有文件 建议用 Android原生机型 三星小米也都没有问题. 这里自己可以自定义扫描过滤写自定义的文件管理器


Ohter Code:

三个核心类分享完了 我们再来说说其他相关代码

application 类中注册消息类型 和 注册消息模板. 这步简单但是很容易忘记
1
2
3
<code class="language-java hljs ">RongIM.registerMessageType(FileMessage.class);
RongIM.registerMessageTemplate(newFileMessageProvider());</code>
点击消息的监听内判断文件消息
1
2
3
4
5
6
7
8
9
10
11
<code class="language-java hljs ">if(message.getContent() instanceof FileMessage) {
            FileMessage fileMessage = (FileMessage) message.getContent();
            if (message.getMessageDirection().equals(io.rong.imlib.model.Message.MessageDirection.RECEIVE)) {
                Intent intent = new Intent(context, FileActivity.class);
                intent.putExtra("photo", fileMessage.getLocalUri() == null fileMessage.getRemoteUri() : fileMessage.getLocalUri());
                if (fileMessage.getThumUri() != null)
                    intent.putExtra("thumbnail", fileMessage.getThumUri());
                context.startActivity(intent);
            }
        }</code>

此处判断是文件消息然后跳转至下载页面下载


总结:

实现效果 和 实现原理 还有大概代码都已经在上方分享,但是代码建议大家不要全部 copy 有些东西还是需要自己理解后亲自去实现. 例如上传和下载的方法实现 这个是和你 服务端 或者 云存储交互的逻辑, 最后也希望这篇文章能够帮助大家! End~

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多