123下一页
返回列表 发新帖

超星图床(网页版及安卓版源码+接口分析)

[复制链接]

33

主题

1663

帖子

5677

积分

硕士生

小北IT

Rank: 6Rank: 6

金币
1667
好评
29
贡献
1

2019-5.1节快乐MT论坛最佳新人MT论坛帅哥考神MT论坛活跃会员

QQ
发表于 3 天前 | 显示全部楼层 | 阅读模式
很久很久之前,@涛之雨 就想跟我一块搞个超星图床工具,因为那时候云盘刚被玩,很多人还不知道它家的操作有多骚。

拖啊拖,拖了好几个月,主要是最近sm.ms图床表现实在让人抓狂,所以就看了下超星的图片上传接口。

吾爱有人写了网页版的,我用他那份源码搭了一个,传送门:

免登录超星图床

源码下载:






Tips:下边的图片,全部都是用的超星的图床。如果你看到本帖时图片还能正常加载,说明它还活着。

为了方便看清楚全局效果,我截图前缩放了页面尺寸,效果图:



大家应该能猜出来,它在手机上的使用体验会非常差劲。

所以我打算分析接口,整个安卓版的(额咳,的确做出来了)。正所谓破解、开发两不误,自己写一个不也挺好的吗?

请求地址:http://notice.chaoxing.com/pc/files/uploadNoticeFile

post请求,请求参数名为attrFile,内容为图片。

以下是php里的写法:



安卓系统提供了非常多的接口,还有非常多的权限,足以打造一个bug十足的app,但我还是拼了。
注:自己问度娘如何动态申请读取存储权限,不会的话再来找我。另外,以下基于AS开发,AIDE的话、你可以考虑用HttpURLConnection(不需要导入依赖)

布局文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     android:background="@drawable/blue"
  6.     android:gravity="center"
  7.     android:orientation="vertical">

  8.     <LinearLayout
  9.         android:layout_width="match_parent"
  10.         android:layout_height="wrap_content"
  11.         android:layout_marginStart="5dp"
  12.         android:layout_marginEnd="5dp"
  13.         android:layout_marginBottom="50dp"
  14.         android:clipToPadding="true"
  15.         android:orientation="vertical">

  16.         <TextView
  17.             android:id="@+id/tv01"
  18.             style="@style/PicBedOne"
  19.             android:layout_gravity="center"
  20.             android:layout_marginBottom="25dp" />

  21.         <TextView
  22.             android:id="@+id/tv02"
  23.             style="@style/PicBedOne"
  24.             android:layout_gravity="center"
  25.             android:layout_marginBottom="25dp" />

  26.         <TextView
  27.             android:id="@+id/tv03"
  28.             style="@style/PicBedOne"
  29.             android:layout_gravity="center" />

  30.     </LinearLayout>

  31.     <TableLayout
  32.         android:layout_width="match_parent"
  33.         android:layout_height="wrap_content"
  34.         android:layout_gravity="center_vertical"
  35.         android:layout_marginBottom="50dp"
  36.         android:background="@android:color/transparent"
  37.         android:orientation="vertical"
  38.         android:stretchColumns="*">

  39.         <TableRow
  40.             android:layout_width="match_parent"
  41.             android:background="@android:color/transparent">

  42.             <Button
  43.                 android:id="@+id/btn01"
  44.                 style="@style/toolOne"
  45.                 android:text="@string/picbed01" />

  46.             <Button
  47.                 android:id="@+id/btn02"
  48.                 style="@style/toolOne"
  49.                 android:text="@string/picbed02" />
  50.         </TableRow>

  51.     </TableLayout>

  52. </LinearLayout>
复制代码


drawable/blue.xml(渐变色背景):

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android">
  3.     <gradient
  4.         android:angle="135"
  5.         android:centerColor="#a1c4fd"
  6.         android:endColor="#8ec5fc"
  7.         android:startColor="#e0c3fc" />
  8. </shape>
复制代码


style.xml里的自定义样式:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>

  3.     <style name="toolOne">
  4.         <item name="android:layout_weight">1.0</item>
  5.         <item name="android:background">@drawable/btnyuan</item>
  6.         <item name="android:layout_width">100dp</item>
  7.         <item name="android:textSize">15sp</item>
  8.         <item name="android:textColor">#F5F5F5</item>
  9.         <item name="android:clickable">true</item>
  10.         <item name="android:layout_height">60dp</item>
  11.         <item name="android:layout_marginStart">5dp</item>
  12.         <item name="android:layout_marginEnd">5dp</item>
  13.         <item name="android:textAllCaps">false</item>
  14.     </style>

  15.     <style name="PicBedOne">
  16.         <item name="android:background">@drawable/btnyuan</item>
  17.         <item name="android:textSize">14sp</item>
  18.         <item name="android:textColor">#F5F5F5</item>
  19.         <item name="android:clickable">true</item>
  20.         <item name="android:gravity">center</item>
  21.         <item name="android:layout_width">match_parent</item>
  22.         <item name="android:layout_height">70dp</item>
  23.         <item name="android:textAllCaps">false</item>
  24.         <item name="android:paddingLeft">5dp</item>
  25.         <item name="android:paddingRight">5dp</item>
  26.         <item name="android:maxLines">1</item>
  27.     </style>
  28. </resources>
复制代码


strings.xml就不贴了,俩按钮里的文字而已,一个叫“选取”,另一个叫“上传”。

然后是工具类(选取图片,返回文件路径用的):

  1. package com.chati.app;

  2. import android.content.Context;
  3. import android.database.Cursor;
  4. import android.net.Uri;
  5. import android.provider.DocumentsContract;
  6. import android.provider.MediaStore;

  7. public class ContentUriUtil {

  8.     public static String getPath(final Context context, final Uri uri) {
  9.         if (DocumentsContract.isDocumentUri(context, uri)) {
  10.             if (isMediaDocument(uri)) {
  11.                 final String docId = DocumentsContract.getDocumentId(uri);
  12.                 final String[] split = docId.split(":");
  13.                 final String type = split[0];
  14.                 Uri contentUri = null;
  15.                 if ("image".equals(type)) {
  16.                     contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
  17.                 } else if ("video".equals(type)) {
  18.                     contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
  19.                 } else if ("audio".equals(type)) {
  20.                     contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
  21.                 }
  22.                 final String selection = "_id=?";
  23.                 final String[] selectionArgs = new String[]{
  24.                         split[1]
  25.                 };
  26.                 return getDataColumn(context, contentUri, selection, selectionArgs);
  27.             }
  28.         } else if ("file".equalsIgnoreCase(uri.getScheme())) {
  29.         return uri.getPath();
  30.         }
  31.         return null;
  32.     }

  33.     public static String getDataColumn(Context context, Uri uri, String selection,
  34.                                        String[] selectionArgs) {
  35.         Cursor cursor = null;
  36.         final String column = "_data";
  37.         final String[] projection = {
  38.                 column
  39.         };

  40.         try {
  41.             cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
  42.                     null);
  43.             if (cursor != null && cursor.moveToFirst()) {
  44.                 final int index = cursor.getColumnIndexOrThrow(column);
  45.                 return cursor.getString(index);
  46.             }
  47.         } finally {
  48.             if (cursor != null)
  49.                 cursor.close();
  50.         }
  51.         return null;
  52.     }

  53.     public static boolean isMediaDocument(Uri uri) {
  54.         return "com.android.providers.media.documents".equals(uri.getAuthority());
  55.     }
  56. }

复制代码


注意把第一行的包名改成你软件的哈。

然后就是选取、上传这个界面的java文件啦,代码如下:

  1. package com.chati.app;

  2. import android.Manifest;
  3. import android.annotation.SuppressLint;
  4. import android.app.Activity;
  5. import android.content.ActivityNotFoundException;
  6. import android.content.ClipData;
  7. import android.content.ClipboardManager;
  8. import android.content.Context;
  9. import android.content.Intent;
  10. import android.content.pm.PackageManager;
  11. import android.graphics.Color;
  12. import android.net.Uri;
  13. import android.os.Build;
  14. import android.os.Bundle;
  15. import android.os.Handler;
  16. import android.os.Message;
  17. import android.view.View;
  18. import android.view.WindowManager;
  19. import android.widget.Button;
  20. import android.widget.TextView;
  21. import android.widget.Toast;

  22. import androidx.annotation.NonNull;
  23. import androidx.annotation.Nullable;
  24. import androidx.core.app.ActivityCompat;

  25. import org.jetbrains.annotations.NotNull;
  26. import org.json.JSONException;
  27. import org.json.JSONObject;

  28. import java.io.File;
  29. import java.io.IOException;
  30. import java.util.Objects;

  31. import okhttp3.Call;
  32. import okhttp3.Callback;
  33. import okhttp3.MediaType;
  34. import okhttp3.MultipartBody;
  35. import okhttp3.OkHttpClient;
  36. import okhttp3.Request;
  37. import okhttp3.RequestBody;
  38. import okhttp3.Response;

  39. public class PicbedActivity extends Activity {

  40.     private String sc;
  41.     private static final int REQUEST_EXTERNAL_STORAGE = 1;
  42.     private static String[] PERMISSIONS_STORAGE = {
  43.             Manifest.permission.READ_EXTERNAL_STORAGE,
  44.             Manifest.permission.WRITE_EXTERNAL_STORAGE
  45.     };
  46.     private String filePath;

  47.     @SuppressLint({"HardwareIds", "SdCardPath"})
  48.     @Override
  49.     protected void onCreate(Bundle savedInstanceState) {
  50.         super.onCreate(savedInstanceState);
  51.         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  52.         getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
  53.         if (Build.VERSION.SDK_INT >= 21) {
  54.             getWindow().setStatusBarColor(Color.TRANSPARENT);
  55.             getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  56.         }
  57.         int option = getWindow().getDecorView().getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
  58.         getWindow().getDecorView().setSystemUiVisibility(option);
  59.         setContentView(R.layout.picbed);

  60.         final TextView tv01 = findViewById(R.id.tv01);
  61.         final TextView tv02 = findViewById(R.id.tv02);
  62.         final TextView tv03 = findViewById(R.id.tv03);
  63.         final Button btn01 = findViewById(R.id.btn01);
  64.         final Button btn02 = findViewById(R.id.btn02);

  65.         tv01.setOnClickListener(v -> {
  66.             sc = tv01.getText().toString();
  67.             if (sc.length() > 0) {
  68.                 copyString(sc);
  69.                 tipToast("Copied!");
  70.             }
  71.         });

  72.         tv02.setOnClickListener(v -> {
  73.             sc = tv02.getText().toString();
  74.             if (sc.length() > 0) {
  75.                 copyString(sc);
  76.                 tipToast("Copied!");
  77.             }
  78.         });

  79.         btn01.setOnClickListener(v -> {
  80.             int permission = ActivityCompat.checkSelfPermission(PicbedActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
  81.             if (permission == PackageManager.PERMISSION_GRANTED) {
  82.                 openSystemFile();
  83.             } else {
  84.                 ActivityCompat.requestPermissions(
  85.                         PicbedActivity.this,
  86.                         PERMISSIONS_STORAGE,
  87.                         REQUEST_EXTERNAL_STORAGE
  88.                 );
  89.             }
  90.         });

  91.         btn02.setOnClickListener(v -> {
  92.             @SuppressLint("HandlerLeak") final Handler handler = new Handler() {
  93.                 @Override
  94.                 public void handleMessage(@NonNull Message msg) {
  95.                     super.handleMessage(msg);
  96.                     sc = Objects.requireNonNull(msg.getData().get("content")).toString();
  97.                     copyString(sc);
  98.                     tv01.setText(sc);
  99.                     sc = "![](" + sc + ")";
  100.                     tv02.setText(sc);
  101.                     tv03.setText("处理完毕");
  102.                 }
  103.             };
  104.             if (filePath != null) {
  105.                 tv03.setText(filePath);
  106.                 File upFile = new File(filePath);
  107.                 OkHttpClient client = new OkHttpClient();
  108.                 RequestBody fileBody = RequestBody.create(upFile, MediaType.parse("image/png"));
  109.                 RequestBody requestBody = new MultipartBody.Builder()
  110.                         .setType(MultipartBody.FORM)
  111.                         .addFormDataPart("attrFile", upFile.getName(), fileBody)
  112.                         .build();

  113.                 Request request = new Request.Builder()
  114.                         .url("http://notice.chaoxing.com/pc/files/uploadNoticeFile")
  115.                         .post(requestBody)
  116.                         .build();
  117.                 Call call = client.newCall(request);
  118.                 call.enqueue(new Callback() {
  119.                     @Override
  120.                     public void onFailure(@NotNull Call call, @NotNull IOException e) {
  121.                         copyString("failed");
  122.                     }

  123.                     @Override
  124.                     public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
  125.                         String result01 = Objects.requireNonNull(response.body()).string();
  126.                         JSONObject json01;
  127.                         try {
  128.                             json01 = new JSONObject(result01);
  129.                             sc = json01.getString("url");
  130.                         } catch (JSONException e) {
  131.                             sc = "处理返回数据失败";
  132.                         }
  133.                         Bundle bd = new Bundle();
  134.                         bd.putString("content", sc);
  135.                         Message message = new Message();
  136.                         message.setData(bd);
  137.                         handler.sendMessage(message);
  138.                     }
  139.                 });
  140.             } else {
  141.                 tipToast("please try again");
  142.             }
  143.         });
  144.     }

  145.     public void openSystemFile() {
  146.         Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
  147.         intent.setType("image/*");
  148.         intent.addCategory(Intent.CATEGORY_DEFAULT);
  149.         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
  150.         try {
  151.             startActivityForResult(Intent.createChooser(intent, "请选择文件"), 1);
  152.         } catch (ActivityNotFoundException e) {
  153.             tipToast("请安装文件管理器");
  154.         }
  155.     }

  156.     @Override
  157.     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
  158.         super.onActivityResult(requestCode, resultCode, data);
  159.         if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
  160.             try {
  161.                 Uri uri = Objects.requireNonNull(data).getData();
  162.                 if (null != uri) {
  163.                     filePath = ContentUriUtil.getPath(this, uri);
  164.                     if (filePath != null) {
  165.                         tipToast("已选取图片");
  166.                     } else {
  167.                         tipToast("选取失败");
  168.                     }
  169.                 }
  170.             } catch (Exception e) {
  171.                 tipToast("选取失败");
  172.             }
  173.         }
  174.     }

  175.     public void copyString(String copyStr) {
  176.         ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
  177.         ClipData mClipData = ClipData.newPlainText("Label", copyStr);
  178.         if (cm != null) {
  179.             cm.setPrimaryClip(mClipData);
  180.         }
  181.     }

  182.     public void tipToast(String tipStr) {
  183.         Toast.makeText(getBaseContext(), tipStr, Toast.LENGTH_SHORT).show();
  184.     }

  185. }
复制代码


同样不要忘了改第一行的包名。

然后去清单文件注册Activity吧:

  1. <activity
  2.             android:name=".PicbedActivity"
  3.             android:configChanges="keyboardHidden|orientation|screenSize" />
复制代码


哦吼,还要导入依赖,在build.gradle文件,添加:

  1. dependencies {
  2.     implementation fileTree(dir: "libs", include: ["*.jar"])
  3.     implementation 'androidx.annotation:annotation:1.1.0'
  4.     implementation 'com.squareup.okhttp3:okhttp:4.8.0'
  5.     implementation 'com.squareup.okio:okio:2.7.0'

  6. }
复制代码


运行界面:



选取图片后会有提示语(已选取或please try again),然后点击上传即可,会自动复制纯链接的结果。也可点击复制Markdown格式的。示例图:



最后贴一下接口调用返回的数据叭,示例图:



软件成品暂时就先不发了,使用前需要付费授权验证(几块钱),等免费开放后我再发给大家吧。


游客,如果您要查看本帖隐藏内容请回复


溜了溜了,下次发帖不知道是啥时候了。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
已有1人评分金币 理由
SamChai + 1 这个php的免登陆有致命bug 千万别用!!! 好.

查看全部评分 总评分:金币 +1 

小北IT创始人
回复

使用道具 举报

33

主题

1663

帖子

5677

积分

硕士生

小北IT

Rank: 6Rank: 6

金币
1667
好评
29
贡献
1

2019-5.1节快乐MT论坛最佳新人MT论坛帅哥考神MT论坛活跃会员

QQ
发表于 3 天前 | 显示全部楼层
开发不易,开源更不易,且用且珍惜,撤了撤了
小北IT创始人
回复

使用道具 举报

7

主题

539

帖子

1727

积分

高中生

Rank: 4

金币
460
好评
6
贡献
0

MT论坛帅哥考神MT论坛最佳新人

发表于 3 天前 | 显示全部楼层
过来看看
回复

使用道具 举报

69

主题

2804

帖子

1万

积分

版主

论坛萌新

Rank: 7Rank: 7Rank: 7

金币
3151
好评
143
贡献
1

考神MT论坛帅哥MT论坛活跃会员MT论坛最佳新人MT论坛侠客

QQ
发表于 3 天前 来自手机  | 显示全部楼层
骚操作
回复

使用道具 举报

33

主题

1663

帖子

5677

积分

硕士生

小北IT

Rank: 6Rank: 6

金币
1667
好评
29
贡献
1

2019-5.1节快乐MT论坛最佳新人MT论坛帅哥考神MT论坛活跃会员

QQ
发表于 3 天前 | 显示全部楼层

下午把apk里的页面进入方式改一下,去掉授权验证,免费提供给大家
小北IT创始人
回复

使用道具 举报

13

主题

1619

帖子

5440

积分

硕士生

Rank: 6Rank: 6

金币
1585
好评
23
贡献
0

考神MT论坛帅哥MT论坛最佳新人

发表于 3 天前 来自手机  | 显示全部楼层
一直在用康哥的云图床
回复

使用道具 举报

33

主题

1663

帖子

5677

积分

硕士生

小北IT

Rank: 6Rank: 6

金币
1667
好评
29
贡献
1

2019-5.1节快乐MT论坛最佳新人MT论坛帅哥考神MT论坛活跃会员

QQ
发表于 3 天前 | 显示全部楼层
Away84 发表于 2020-8-2 15:35
一直在用康哥的云图床

没精力折腾私有图床,带宽是个问题
小北IT创始人
回复

使用道具 举报

31

主题

773

帖子

2405

积分

大学生

Rank: 5Rank: 5

金币
561
好评
0
贡献
0
发表于 3 天前 来自手机  | 显示全部楼层
谢谢分享
回复

使用道具 举报

1

主题

453

帖子

1469

积分

高中生

Rank: 4

金币
209
好评
0
贡献
0
QQ
发表于 3 天前 来自手机  | 显示全部楼层
谢谢楼主分享
回复

使用道具 举报

1

主题

43

帖子

200

积分

初中生

Rank: 3Rank: 3

金币
37
好评
0
贡献
0
发表于 3 天前 来自手机  | 显示全部楼层
谢谢分享,看不懂
回复

使用道具 举报

101

主题

3594

帖子

7163

积分

硕士生

...

Rank: 6Rank: 6

金币
19
好评
25
贡献
0
发表于 3 天前 来自手机  | 显示全部楼层
感谢分享
回复

使用道具 举报

6

主题

1914

帖子

4811

积分

大学生

Rank: 5Rank: 5

金币
636
好评
1
贡献
0
发表于 3 天前 来自手机  | 显示全部楼层
感谢分享
回复

使用道具 举报

86

主题

3098

帖子

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
2704
好评
44
贡献
19

MT论坛最佳新人MT论坛帅哥考神MT论坛活跃会员

QQ
发表于 3 天前 来自手机  | 显示全部楼层
sm.ms 还行
回复

使用道具 举报

34

主题

1413

帖子

3998

积分

大学生

柒宇

Rank: 5Rank: 5

金币
1109
好评
16
贡献
0

MT论坛帅哥2019-5.1节快乐考神MT论坛最佳新人MT论坛活跃会员

发表于 3 天前 来自手机  | 显示全部楼层
感谢分享
回复

使用道具 举报

33

主题

1663

帖子

5677

积分

硕士生

小北IT

Rank: 6Rank: 6

金币
1667
好评
29
贡献
1

2019-5.1节快乐MT论坛最佳新人MT论坛帅哥考神MT论坛活跃会员

QQ
发表于 3 天前 | 显示全部楼层

速度经常抽风
小北IT创始人
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表