Andoid - 开发实例(5):上传文件实现方法

本文介绍如何使用Android客户端向服务器上传图片。重点讲解了multipart/form-data协议,并提供了具体示例代码,帮助开发者理解并实现文件上传功能。
一月份做项目时 Android 客户端需要上传图片,小小的研究了下,不改独享,分享!

如果要在客户端向服务器上传文件,我们就必须模拟一个POST multipart/form-data类型的请求,Content-Type必须是multipart/form-data
这篇详细介绍这个 ENCTYPE="multipart/form-data" 协议,来解决自己的Java程序向服务器传文件的问题(比如:安卓程序上传图片)

ENCTYPE="multipart/form-data" :通过 http 协议上传文件 rfc1867协议,客户端发送内容构造

<form action="picture/upload.do" method="POST" ENCTYPE="multipart/form-data">
    <input type="text" name="users_id">
    <input type="text" name="users_name">
    <input type="file" name="image_path">
    <input type="submit">
</form>
使用上面这个表单,抓包的结果如下
POST /picture/upload.do HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Referer: http://192.168.1.98:8080/uploadpic.jsp
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 
Content-Type: multipart/form-data; boundary=---------------------------7dd36df20300
Accept-Encoding: gzip, deflate
Host: 192.168.1.98:8080
Content-Length: 529762
Connection: Keep-Alive
Cache-Control: no-cache

-----------------------------7dd36df20300
Content-Disposition: form-data; name="users_id"
<!--空行-->
1
-----------------------------7dd36df20300
Content-Disposition: form-data; name="users_name"
<!--空行-->
admin
-----------------------------7dd36df20300
Content-Disposition: form-data; name="image_path"; filename="S21223-170317.jpg"
Content-Type: image/pjpeg
<!--空行-->
<!--文件 byte 数据-->
-----------------------------7dd36df20300--
<!--空行-->

详细解析:
Content-Type: multipart/form-data; boundary=---------------------------7dd36df20300
根据 rfc1867, multipart/form-data是必须的。
---------------------------7dd36df20300  是分隔符,分隔多个文件、表单项。
其中 d36df20300 是即时生成的一个字符,用以确保整个分隔符不会在文件或表单项的内容中出现。
---------------------------7d    IE内核标志
---------------------------71    Mozila内核标志
从请求体里可以看出每一条数据后面都有换行 \r\n ,并且 Parameter 和 value 之间还有个空行
最后把格式提取:
--分隔符 \r\n
Content-Disposition: form-data; name="[Parameter]" \r\n
\r\n
[value] \r\n
--分隔符 \r\n
Content-Disposition: form-data; name="[Parameter]"; filename="[]" \r\n
Content-Type: image/pjpeg \r\n
\r\n
<!--文件 byte 数据--> \r\n
--分隔符-- \r\n
\r\n

了解了传输格式,接下来就是程序实现:

String path = "http://192.168.1.102:8080/picture/upload.do";
// 换行符
String enter = "\r\n";
// 分割线
String boundary = "---------------------------7dd36df20300";
StringBuffer sb = new StringBuffer();
// 获取文件流
File file = new File("D:\\123.jpg");
FileInputStream is = new FileInputStream(file);
// 参数信息
String[] keys = { "users_id", "users_name" };
String[] values = { "1", "admin" };
// 拼凑参数
for (int i = 0; i < keys.length; i++) {
    sb.append("--" + boundary + enter);
    sb.append("Content-Disposition: form-data; name=\"" + keys[i] + "\"");
    sb.append(enter + enter);
    sb.append(values[i]);
    sb.append(enter);
}
// 拼凑文件头部信息
sb.append("--" + boundary);
sb.append(enter);
sb.append("Content-Disposition: form-data; name=\"image_path\"; filename=\"123.jpg\"");
sb.append(enter);
sb.append("Content-Type: image/*");
sb.append(enter + enter);
byte[] data = sb.toString().getBytes();
byte[] end_data = (enter + "--" + boundary + "--" + enter + enter).getBytes();
// 初始化 HttpURLConnection
URL url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(3000);
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
con.setRequestMethod("POST");
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Charset", "UTF-8");
con.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
con.setRequestProperty("Content-Length", file.length() + data.length 
+ end_data.length + "");
// 开始上传
OutputStream outputStream = con.getOutputStream();
outputStream.write(data);
byte[] fileBuffer = new byte[1024];
int length = -1;
while ((length = is.read(fileBuffer)) != -1) {
    outputStream.write(fileBuffer, 0, length);
}
is.close();
outputStream.write(end_data);
outputStream.flush();
outputStream.close();
con.getResponseCode();



在 Android 中,`FlexboxLayout` 本身并不直接支持滑动效果(如水平或垂直滚动),但可以通过将其嵌套在 `ScrollView` 或 `HorizontalScrollView` 中来实现滑动功能。这种方式适用于内容超出屏幕宽度或高度时需要滑动查看的场景。 ### 水平滑动 若希望 `FlexboxLayout` 实现 **水平滑动**,可以将其包裹在 `HorizontalScrollView` 中,并设置 `flexDirection` 属性为 `row`(默认值),以确保子视图沿水平方向排列[^3]。 ```xml <HorizontalScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <com.google.android.flexbox.FlexboxLayout android:layout_width="wrap_content" android:layout_height="wrap_content" app:flexDirection="row"> <!-- 子视图 --> <TextView android:layout_width="120dp" android:layout_height="48dp" android:text="Item 1" android:background="@color/colorPrimary" /> <TextView android:layout_width="120dp" android:layout_height="48dp" android:text="Item 2" android:background="@color/colorAccent" /> <!-- 更多子视图 --> </com.google.android.flexbox.FlexboxLayout> </HorizontalScrollView> ``` ### 垂直滑动 若希望 `FlexboxLayout` 实现 **垂直滑动**,可将其包裹在 `ScrollView` 中,并将 `flexDirection` 设置为 `column`,这样所有子视图会沿垂直方向排列[^3]。 ```xml <ScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <com.google.android.flexbox.FlexboxLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:flexDirection="column"> <!-- 子视图 --> <TextView android:layout_width="match_parent" android:layout_height="48dp" android:text="Item 1" android:background="@color/colorPrimary" /> <TextView android:layout_width="match_parent" android:layout_height="48dp" android:text="Item 2" android:background="@color/colorAccent" /> <!-- 更多子视图 --> </com.google.android.flexbox.FlexboxLayout> </ScrollView> ``` ### 动态控制滑动行为 如果希望在代码中动态控制滑动行为,例如监听滑动事件或手动触发滑动,可以在 Java/Kotlin 中结合 `View.post()` 和 `scrollTo()` 方法实现。例如: ```kotlin val flexboxLayout = findViewById<FlexboxLayout>(R.id.flexbox_layout) flexboxLayout.post { // 水平滑动到特定位置 flexboxLayout.scrollTo(100, 0) } ``` 此外,也可以使用 `NestedScrollView` 替代 `ScrollView` 或 `HorizontalScrollView`,以便更好地支持与 `CoordinatorLayout` 等组件的交互,尤其是在涉及复杂 UI 结构时[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值